Near Drop
NEAR Drop is a smart contract that allows users to create token drops ($NEAR, Fungible and Non-Fungible Tokens), and link them to specific private keys. Whoever has the private key can claim the drop into an existing account, or ask the contract to create a new one for them.
Particularly, it shows:
- How to create a token drops (NEAR, FT and NFT)
- How to leverage Function Call keys for enabling amazing UX
This example showcases a simplified version of the contract that both Keypom and the Token Drop Utility use to distribute tokens to users
Contract Overview​
The contract exposes 3 methods to create drops of NEAR tokens, FT, and NFT. To claim the tokens, the contract exposes two methods, one to claim in an existing account, and another that will create a new account and claim the tokens into it.
This contract leverages NEAR unique feature of FunctionCall keys, which allows the contract to create new accounts and claim tokens on behalf of the user.
Imagine Alice want to drop some NEAR to Bob:
- Alice will call
create_near_drop
passing some NEAR amount, and a Public Access Key - The Contract will check if Alice attached enough tokens and create the drop
- The Contract will add the
PublicKey
as aFunctionCall Key
to itself, that only allow to call the claim methods - Alice will give the
Private Key
to Bob - Bob will use the Key to sign a transaction calling the
claim_for
method - The Contract will check if the key is linked to a drop, and if it is, it will send the drop
It is important to notice that, in step (5), Bob will be using the Contract's account to sign the transaction, and not his own account. Remember that in step (3) the contract added the key to itself, meaning that anyone with the key can call the claim methods in the name of the contract.
Contract's interface
create_near_drop(public_keys, amount_per_drop)
​
Creates #public_keys
drops, each with amount_per_drop
NEAR tokens on them
create_ft_drop(public_keys, ft_contract, amount_per_drop)
​
Creates #public_keys
drops, each with amount_per_drop
FT tokens, corresponding to the ft_contract
create_nft_drop(public_key, nft_contract)
​
Creates a drop with an NFT token, which will come from the nft_contract
claim_for(account_id)
​
Claims a drop, which will be sent to the existing account_id
create_account_and_claim(account_id)
​
Creates the account_id
, and then drops the tokens into it
Contract's State​
We can see in the contract's state that the contract keeps track of different PublicKeys
, and links them to a specific DropId
, which is simply an identifier for a Drop
(see bellow).
top_level_account
: The account that will be used to create new accounts, generally it will betestnet
ormainnet
next_drop_id
: A simple counter used to assign unique identifiers to each dropdrop_id_by_key
: AMap
betweenPublicKey
andDropId
, which allows the contract to know what drops are claimable by a given keydrop_by_id
: A simpleMap
that links eachDropId
with the actualDrop
data.
Loading...
Drop Types​
There are 3 types of drops, which differ in what the user will receive when they claims the corresponding drop - NEAR, fungible tokens (FTs) or non-fungible tokens (NFTs).
- drop_types.rs
- near_drop.rs
- ft_drop.rs
- nft_drop.rs
Loading...
Loading...
Loading...
Loading...
Notice that in this example implementation users cannot mix drops. This is, you can either drop NEAR tokens, or FT, or NFTs, but not a mixture of them (i.e. you cannot drop 1 NEAR token and 1 FT token in the same drop)
Create a drop​
All create
start by checking that the user deposited enough funds to create the drop, and then proceed to add the access keys to the contract's account as FunctionCall Keys.
- NEAR Drop
- FT Drop
- NFT Drop
- create_near_drop
- near_drop
Loading...
Loading...
- create_ft_drop
- ft_drop
Loading...
Loading...
- create_nft_drop
- nft_drop
Loading...
Loading...
Storage Costs​
While we will not go into the details of how the storage costs are calculated, it is important to know what is being taken into account:
- The cost of storing each Drop, which will include storing all bytes associated with the
Drop
struct - The cost of storing each
PublicKey -> DropId
relation in the maps - Cost of storing each
PublicKey
in the account
Notice that (3) is not the cost of storing the byte representation of the PublicKey
on the state, but the cost of adding the key to the contract's account as a FunctionCall key.
Claim a drop​
In order to claim drop, a user needs to sign a transaction using the Private Key
, which is the counterpart of the Public Key
that was added to the contract.
All Drops
have a counter
which decreases by 1 each time a drop is claimed. This way, when all drops are claimed (counter
== 0), we can remove all information from the Drop.
There are two ways to claim a drop: claim for an existing account and claim for a new account. The main difference between them is that the first one will send the tokens to an existing account, while the second one will create a new account and send the tokens to it.
- Existing Account
- New Account
- claim_for
- internal_claim
Loading...
Loading...
- create_account_and_claim
- resolve_account_create
- internal_claim
Loading...
Loading...
Loading...
Testing the Contract​
The contract readily includes a sandbox testing to validate its functionality. To execute the tests, run the following command:
- 🦀 Rust
cargo test
The integration tests
use a sandbox to create NEAR users and simulate interactions with the contract.
Deploying the Contract to the NEAR network​
In order to deploy the contract you will need to create a NEAR account.
- Short
- Full
# Create a new account pre-funded by a faucet
near create-account <accountId> --useFaucet
# Create a new account pre-funded by a faucet
near account create-account sponsor-by-faucet-service <my-new-dev-account>.testnet autogenerate-new-keypair save-to-keychain network-config testnet create
Then build and deploy the contract:
cargo near build
cargo near deploy <accountId> with-init-call new json-args '{"top_level_account": "testnet"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send
CLI: Interacting with the Contract​
To interact with the contract through the console, you can use the following commands:
- Short
- Full
# create a NEAR drop
near call <account-id> create_near_drop '{"public_keys": ["ed25519:AvBVZDQrg8pCpEDFUpgeLYLRGUW8s5h57NGhb1Tc4H5q", "ed25519:4FMNvbvU4epP3HL9mRRefsJ2tMECvNLfAYDa9h8eUEa4"], "amount_per_drop": "10000000000000000000000"}' --accountId <account-id> --deposit 1 --gas 100000000000000
# create a FT drop
near call <account-id> create_ft_drop '{"public_keys": ["ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "ed25519:5oN7Yk7FKQMKpuP4aroWgNoFfVDLnY3zmRnqYk9fuEvR"], "amount_per_drop": "1", "ft_contract": "<ft-contract-account-id>"}' --accountId <account-id> --gas 100000000000000
# create a NFT drop
near call <account-id> create_nft_drop '{"public_key": "ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "nft_contract": "<nft-contract-account-id>"}' --accountId <account-id> --gas 100000000000000
# claim to an existing account
# see the full version
# claim to a new account
# see the full version
# create a NEAR drop
near contract call-function as-transaction <account-id> create_near_drop json-args '{"public_keys": ["ed25519:AvBVZDQrg8pCpEDFUpgeLYLRGUW8s5h57NGhb1Tc4H5q", "ed25519:4FMNvbvU4epP3HL9mRRefsJ2tMECvNLfAYDa9h8eUEa4"], "amount_per_drop": "10000000000000000000000"}' prepaid-gas '100.0 Tgas' attached-deposit '1 NEAR' sign-as <account-id> network-config testnet sign-with-keychain send
# create a FT drop
near contract call-function as-transaction <account-id> create_ft_drop json-args '{"public_keys": ["ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "ed25519:5oN7Yk7FKQMKpuP4aroWgNoFfVDLnY3zmRnqYk9fuEvR"], "amount_per_drop": "1", "ft_contract": "<ft-contract-account-id>"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as <account-id> network-config testnet sign-with-keychain send
# create a NFT drop
near contract call-function as-transaction <account-id> create_nft_drop json-args '{"public_key": "ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "nft_contract": "<nft-contract-account-id>"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as <account-id> network-config testnet sign-with-keychain send
# claim to an existing account
near contract call-function as-transaction <account-id> claim_for json-args '{"account_id": "<claimer-account-id>"}' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' sign-as <account-id> network-config testnet sign-with-plaintext-private-key --signer-public-key ed25519:AvBVZDQrg8pCpEDFUpgeLYLRGUW8s5h57NGhb1Tc4H5q --signer-private-key ed25519:3yVFxYtyk7ZKEMshioC3BofK8zu2q6Y5hhMKHcV41p5QchFdQRzHYUugsoLtqV3Lj4zURGYnHqMqt7zhZZ2QhdgB send
# claim to a new account
near contract call-function as-transaction <account-id> create_account_and_claim json-args '{"account_id": "<claimer-account-id>"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as <account-id> network-config testnet sign-with-plaintext-private-key --signer-public-key ed25519:4FMNvbvU4epP3HL9mRRefsJ2tMECvNLfAYDa9h8eUEa4 --signer-private-key ed25519:2xZcegrZvP52VrhehvApnx4McL85hcSBq1JETJrjuESC6v6TwTcr4VVdzxaCReyMCJvx9V4X1ppv8cFFeQZ6hJzU send
At the time of this writing, this example works with the following versions:
- near-cli:
0.17.0
- rustc:
1.82.0