Near Learn

Deadline & claim

Enforce a campaign deadline with env::block_timestamp_ms() and let the creator claim the funds with a NEAR transfer Promise once the goal is reached.

Beginner5 min read3-question check

Campaigns already record a deadline_ms, and pledge refuses late pledges. Now the money has to go somewhere. In this milestone the creator claims everything raised once the deadline has passed and the goal was met.

It is the first time the contract sends NEAR out, which means it is the first time we create a Promise — and the first time we must think about the fact that a transfer on NEAR happens after our method returns.

Time on NEAR#

EVMNEAR (near-sdk)
Current timeblock.timestamp (seconds)env::block_timestamp() (nanoseconds) or env::block_timestamp_ms() (milliseconds)
Typeuint256u64
Who sets itThe block proposerThe block producer; blocks arrive roughly every second
Good forDeadlines of minutes or moreDeadlines of minutes or more
Block time, side by side

We store and compare milliseconds everywhere: deadline_ms = env::block_timestamp_ms() + duration_ms at creation, now < deadline_ms to pledge, now >= deadline_ms to claim or refund. Milliseconds also fit safely in a JavaScript number, which matters when the frontend reads them (lesson 8). As on any chain, do not build logic that depends on second-level precision.

The claim method#

Pay the creator once, after a successful campaign
Solidity
function claim(uint256 id) external {
    Campaign storage c = campaigns[id];
    require(msg.sender == c.creator, "only creator");
    require(block.timestamp >= c.deadline, "running");
    require(c.raised >= c.goal, "goal not reached");
    require(!c.claimed, "already claimed");

    c.claimed = true;                       // effects
    (bool ok, ) = c.creator.call{value: c.raised}("");
    require(ok, "transfer failed");         // atomic: reverts everything
}
Rust
pub fn claim(&mut self, campaign_id: CampaignId) -> Promise {
    let now = env::block_timestamp_ms();
    let campaign = self.campaign_mut(campaign_id);
    require!(env::predecessor_account_id() == campaign.creator, "only the creator can claim");
    require!(now >= campaign.deadline_ms, "campaign is still running");
    require!(campaign.raised >= campaign.goal, "goal not reached");
    require!(!campaign.claimed, "already claimed");

    campaign.claimed = true; // effects before the interaction
    Promise::new(campaign.creator.clone())
        .transfer(NearToken::from_yoctonear(campaign.raised))
}
Coming from EVM

Add claim to the #[near] impl block. The checks are identical; the difference is the last line. In Solidity the transfer happens inside the call and require(ok) can revert everything. On NEAR, Promise::new(..).transfer(..) only schedules a transfer receipt that executes after claim has finished and its state is saved.

  • Returning the `Promise` makes the transaction’s final result depend on the transfer, so wallets and explorers show the claim as failed if the transfer fails. It also lets us chain a callback in the next lesson.
  • `claimed = true` before the transfer. Between claim and its transfer receipt, other transactions can run. Setting the flag first means a second claim in that window hits “already claimed”. This is checks-effects-interactions, and on NEAR it is not optional.
  • No 1 yoctoNEAR requirement. Sensitive methods often demand exactly 1 yoctoNEAR so they cannot be called with a function-call access key. Here the money can only ever go to the creator, so a key that triggers claim cannot redirect it.
MoreDesign choice: why does only the creator claim?

The payout always goes to the creator, so you could let anyone trigger it (a keeper bot, a backer who wants the campaign settled). That is a fine design too — remove the predecessor check and the money still cannot go anywhere else. We keep the check so the creator controls when the payout happens, and so the course has one clear authorization example.

Check yourself

3 questions · progress saved in this browser

  1. 1.What unit does env::block_timestamp() return?
  2. 2.In claim, when does the NEAR actually move to the creator?
  3. 3.Why is claimed set to true before the transfer is scheduled?