logo
Install Learn Run a node Wallets Earn Develop Smart Contracts Use ID
  • Discourse
  • Installation

    • Downloads - Mainnet
    • Downloads - Testnet
    • Verification instructions
  • Learn

    • Learn about Concordium
      • Identities
        • Company identity creation
      • Accounts
      • Glossary of Concordium Terms
  • Concordium Wallets

    • Deciding between the wallets
      • Transactions overview
    • Wallet FAQs
      • Concordium Wallet for Mobile FAQ
      • Concordium Wallet for Web FAQ
    • Set up the Concordium Wallet for Web
    • Set up the Concordium Wallet for Mobile
    • Set up the Concordium Legacy Wallet
    • Create an identity
      • Change identity name
    • Create an account
      • Change account name
    • Send CCD
    • Connect dApps to wallets
      • Proofs and revealing information
    • Tokens in the wallet
    • Shield and unshield CCD on an account
      • Make a shielded transfer on an account
    • Make a backup of identities, accounts, and addresses
    • Export a private key
    • Recover your Wallet
    • Find and share your account address
    • Update your passcode and biometric settings
    • Inspect a release schedule
    • Manage address book
    • Desktop Wallet
      • Set up the Desktop Wallet
        • Set up the LEDGER device and install the Concordium LEDGER App
      • Use the Desktop Wallet
      • Transfer CCD with a schedule in Desktop Wallet
      • Apply a transaction log filter in the Desktop Wallet
      • Update the Desktop Wallet
      • Reset desktop wallet data
      • Shared accounts with multiple credentials in Desktop Wallet
        • Create a credentials file
        • Add credentials to an account
        • Create a multi-signature CCD transfer in the Desktop Wallet
        • Create a scheduled transfer in the Desktop Wallet
        • Sign a transaction proposal
        • View transaction proposals
  • Earn with Concordium

    • How to earn with Concordium
      • Bakers
        • Baker management
        • Add baker
        • Change baker options
        • Run a baker node on Windows
        • Run a baker node on macOS
        • Run a baker node on Ubuntu
        • Run a baker node on Docker
        • Become a baker using the Concordium Client
      • Delegation
        • Delegate to a baker pool or passive delegation
        • Update delegation to a baker pool or passive delegation
        • Remove delegation to a baker pool or passive delegation
      • Delegation and baking FAQ
  • Develop on Concordium

    • Developer resources
      • SDKs and APIs
      • dApp examples
      • Dashboards and Status Pages
      • Auxiliary Tools
      • gRPC V2 documentation
      • gRPC documentation
    • Run a node on Concordium
      • Ubuntu nodes
        • Run a node on a server with Ubuntu on Mainnet
        • Run a node on a server with Ubuntu on Testnet
        • Troubleshoot a node on a server with Ubuntu
      • MacOS nodes
        • Run and manage a node on macOS
        • Troubleshoot a node on MacOS
      • Windows nodes
        • Run and manage a node on Windows
        • Concordium Windows node runner service configuration
        • Troubleshoot a node running on Windows
      • Docker nodes
        • Run a node with Docker
        • Troubleshoot a node on a server with Docker
    • Concordium Client
      • Concordium Client transactions
      • Querying a node
    • Run a local chain
  • Use Concordium's ID Layer

    • Use ID: Create proofs
    • The gallery with ID authentication
      • Writing the verifying backend
      • Interacting with a wallet
      • Setting up the front end
  • Smart Contracts v1

    • Introduction
      • Smart contract modules
      • Smart contract instances
      • Smart contract schemas
      • Developing smart contracts in Rust
    • Quick start guide
    • Best practices
      • Development best practices
    • Tutorials
      • Setup the development environment
      • Counter
      • PiggyBank
        • Writing the piggy bank smart contract
        • Testing the piggy bank smart contract
        • Deploying the piggy bank smart contract
        • Setting up a front end
      • wCCD
        • Understanding the wCCD token protocol
        • Interacting with the wCCD token protocol
        • Setting up the front end
        • Running your first full dApp
      • The Voting dApp
        • Writing a voting smart contract
        • Setting up the front-end
      • Mint an NFT
        • Upload the NFT
        • Intialize, build, and deploy the smart contract
        • Mint and transfer the NFT
      • Mint a semi-fungible token
        • Setup the development environment
        • Smart contract modifications
        • Mint and transfer semi-fungible token
      • Mint fungible tokens
        • Smart contract implementation for fungible tokens
        • Mint, transfer, and burn fungible tokens
      • NFT marketplace smart contract tutorial
        • Build and deploy the smart contract
      • eSealing
        • Exploring the eSealing dapp
      • Sponsored Transactions
        • Exploring the sponsored Transactions Smart Contract
        • Exploring the sponsored Transactions dApp
        • Wallet Connect
      • Smart Contract Upgrade
        • Native upgradability
    • Contract development guides
      • Install tools for development
      • Setting up a smart contract project
      • Compile a Rust smart contract module
      • Return custom errors
      • Locally simulate contract functions
      • Build a contract schema
      • Build using no_std
      • Fallback entrypoints
      • Make a contract upgradeable
      • Work with the JSON parameters
    • Contract testing guides
      • Unit test a contract in Rust
      • Integration test a contract in Rust
    • On-chain guides
      • Deploy a smart contract module
      • Initialize a smart contract instance
      • Interact with a smart contract instance
      • Inspect a smart contract instance
      • Invoke a smart contract instance
    • References
      • Cryptographic primitives
      • Contract host functions
      • Local settings
      • References on-chain
      • Schema JSON representation
      • Simulation contexts
      • Rust contract examples (repo)
      • concordium-std
    • Ethereum developer onboarding
      • FAQs
    • Solana developer onboarding
      • Overview
      • FAQs
  • Smart Contracts v0

    • Introduction
      • Smart contract modules
      • Smart contract instances
      • Smart contract schemas
      • Developing smart contracts in Rust
    • Tutorials
      • Writing the piggy bank smart contract
      • Testing the piggy bank smart contract
    • Contract development guides
      • Install tools for development
      • Setting up a smart contract project
      • Compile a Rust smart contract module
      • Unit test a contract in Rust
      • Return custom errors
      • Locally simulate contract functions
      • Build a contract schema
      • Build using no_std
    • On-chain guides
      • Deploy a smart contract module
      • Initialize a smart contract instance
      • Interact with a smart contract instance
      • Inspect a smart contract instance
    • References
      • Schema JSON representation
      • Simulation contexts
      • Contract host functions
      • References on-chain
      • Local settings
      • Rust contract examples (repo)
      • concordium-std
  • Low-code NFT marketplace

    • Concordium low-code NFT framework
    • Low code NFT marketplace
    • Low-code NFT minting tool
  • Resources

    • CCDScan
      • CCDScan Blocks overview
      • CCDScan Transactions overview
      • CCDScan Accounts overview
      • CCDScan Staking overview
      • CCDScan Nodes overview
    • Release notes - Mainnet
    • Release Notes - Testnet
    • Legal
      • Terms and conditions
      • Terms and Conditions for Concordium Desktop Wallet
      • Terms and Conditions for Concordium Legacy Wallet (previously Concordium Mobile Wallet)
      • Third Party Licenses
      • Third-party Software and Hardware Notices
      • Concordium Wallet for Web Third Party Licenses
      • Concordium Desktop Wallet Third Party Licenses
    • Contact Anonymity Revokers and Identity Providers
On this page
  • permit function
  • supportsPermit function
  • registerPublicKeys function
  • Testing the signature verification

Sponsored Transactions Smart Contract#

The goal of this part of the tutorial is to understand how the sponsored transaction mechanism is implemented in the smart contract and how to create and verify an Ed25519 signature in the smart contract code.

Clone the sponsored transaction example which you will modify in this tutorial.

$git clone --recurse-submodules [email protected]:Concordium/concordium-rust-smart-contracts.git

Navigate to the correct example folder:

$cd ./examples/cis3-nft-sponsored-txs

The cis3_nft contract implements the CIS-3 standard (sponsored transactions). The standard defines that the contract has to expose the permit and the supportsPermit functions.

permit function#

The permit function verifies an ed25519 signature from a sponsoree and authorizes the sponsor to execute the logic of specific entrypoints (transfer/updateOperator in the example code) on behalf of the sponsoree. The sponsored transaction mechanism replaces the authorization checks conducted on the sender/invoker variable with signature verification. That is, the sponsoree needs to sign a message (indicating its intended action), then anyone can invoke the permit function with this signed message to execute the sponsoree’s intended action. The smart contract logic checks if the signature was generated by the sponsoree.

Note

Ed25519 is an elliptic curve signing algorithm using EdDSA and Curve25519. It belongs to asymmetric cryptography, also known as public-key cryptography, where you generate a pair of related keys – one public key and one private key. The sponsoree encrypts/signs a message with its private key and anyone can decrypt the sponsoree’s message using its public key. The public key is shared with other entities (e.g., the smart contract) while the private key is only known to the sponsoree. The smart contract needs to know the public key to be able to verify that the sponsoree signed the message with the corresponding private key.

Note

The Concordium Wallet for Web prepends the message with the account address (signer) and 8 zero bytes before signing it. This ensures that signed messages can be distinguished from transactions that are equally signed by the account address in the Concordium Wallet for Web because transactions don’t start with an account address. The smart contract logic reverse engineers the Concordium Wallet for Web implementation by equally prepending the account address (signer) and 8 zero bytes to the message.

Note

Concordium accounts can be multi-sig and each account has at least one public key and at least one private key associated with it. For this sponsored transactions example, the accounts in the Concordium Wallet for Web (or Concordium Wallet for Mobile) have exactly one public key and exactly one private key (no multi-sig accounts). You use your private key to sign the message in the wallet and your public key is used in the smart contract to verify that this signature was generated in the wallet with the associated private key. Never share your private key.

supportsPermit function#

This function can be queried with a list of given entrypoint names. The response contains corresponding results for each entrypoint, where the result is either “Entrypoint is not supported and can not be invoked via the permit function using the sponsored transaction mechanism” or “Entrypoint is supported and can be invoked via the permit function using the sponsored transaction mechanism”.

You can explore the function by invoking it with the concordium-client as follows:

$concordium-client contract invoke 4376 --entrypoint supportsPermit --parameter-json supportsPermit.json --grpc-port 10000 --grpc-ip node.testnet.concordium.com

For example, this supportsPermit.json file results in the below screenshot.

{"queries":["aaaa","transfer","updateOperator"]}
Output of supportsPermit function

Note

Comprehensive instructions on how to download and set up concordium-client can be found in Setup the development environment.

registerPublicKeys function#

Concordium smart contracts currently have no way to query the corresponding public key(s) of an account within the smart contract code. The Concordium team is working on exposing the public keys in the smart contract code in the next protocol update so that the registration of a public key within the smart contract code will not be necessary anymore in the future. For the time being, Concordium suggests using a public_key_registry that allows a special trusted role/account to register the public keys in the smart contract. The example smart contract implements such a registry. The logic of the code is as such that once an account has a public key registered, the mapping between the public key and the account can not be updated anymore in the public_key_registry as seen below:

// Register the public key.
let old_public_key = host.state_mut().public_key_registry.insert(param.account, (param.public_key, 0));

// Return an error if the owner tries to update/re-write a new public key for an already registered account.
ensure!(old_public_key.is_none(), CustomContractError::AccountAlreadyRegistered.into());

The registry in the smart contracts links an account to a public key and its next nonce. The nonce is increased sequentially every time a signed message (corresponding to the account) is successfully executed in the permit function. The nonce is used to prevent replay attacks of the signed message because only a message signed with the next nonce in sequence can be executed as seen below:

// Check the nonce to prevent replay attacks.
ensure_eq!(message.nonce, nonce, CustomContractError::NonceMismatch.into());

Testing the signature verification#

In this section, you will learn how to create an Ed25519 signature that you can use for your test cases to confirm that the smart contract signature verification logic works as expected.

Note

The commands in this section have to be executed from the ./examples/cis3-nft-sponsored-txs folder where the Cargo.toml file is defined for this project.

Run the test cases to confirm that they pass before you start modifying the code:

$cargo test
or
$cargo concordium test
Running the test cases

You should see no errors and all the test cases are green indicating that they passed successfully.

Note

Comprehensive instructions on how to download and set up cargo concordium can be found in Setup the development environment.

The next few commands explain how you can display the message_hash that you would need to sign.

Add the following to the smart contract permit function after the variable message_hash is defined.

println!("Message hash: {}", HashSha2256(message_hash));

This prints the message_hash when running the test cases as follows:

$cargo test -- --nocapture
Printing the message hash

The test cases have two tests (test_permit_transfer/test_permit_update_operator) that execute the permit function, and that is why you see two message_hashes printed out in the console. You can comment out one of the tests to not get confused about which message you are going to sign. You need to generate a public-private key pair to sign this message_hash. You can use an Ed25519 signature generation tool for testing to generate such keys and signatures.

Note

Only use these online tools that generate private keys for you in your test cases. Don’t use the key in a production setting, since the private key might be known to the online webpage provider and should not be considered safe.

Copy the message_hash into the message field and switch the Msg Encoding to hex (not text).

Click the Generate Random Key button and click the Sign button.

Generate an Ed25519 signature

This action generates a signature in the last field in the above screenshot. The signature and public key in the above screenshot can be copied into the below code snippets to decode them to bytes.

let signature:SignatureEd25519 = "FC87CE9497CBD9DDDFB6CED31914D4FB93DD158EEFE7AF927AB31BB47178E61A33BEA52568475C161EC5B7A5E86B9F5F0274274192665D83197C4CE9A24C7C06".parse().unwrap();
println!("Signature: {:?}", signature.0);

Add the above code snippet to the top of a test case and run the test cases again to output the signature in bytes.

$cargo test -- --nocapture

The output should look similar to:

$signature: [252, 135, 206, 148, 151, 203, 217, 221, 223, 182, 206, 211, 25, 20, 212, 251, 147, 221, 21, 142, 239, 231, 175, 146, 122, 179, 27, 180, 113, 120, 230, 26, 51, 190, 165, 37, 104, 71, 92, 22, 30, 197, 183, 165, 232, 107, 159, 95, 2, 116, 39, 65, 146, 102, 93, 131, 25, 124, 76, 233, 162, 76, 124, 6]

You can create the below signature constant. This signature can be used in test cases to check if your signature verification logic in the smart contract works.

const SIGNATURE: SignatureEd25519 = SignatureEd25519([252, 135, 206, 148, 151, 203, 217, 221, 223, 182, 206, 211, 25, 20, 212, 251, 147, 221, 21, 142, 239, 231, 175, 146, 122, 179, 27, 180, 113, 120, 230, 26, 51, 190, 165, 37, 104, 71, 92, 22, 30, 197, 183, 165, 232, 107, 159, 95, 2, 116, 39, 65, 146, 102, 93, 131, 25, 124, 76, 233, 162, 76, 124, 6]);

The public key from the above screenshot can be converted in a similar fashion.

let public_key:PublicKeyEd25519 = "8728D5F139ABEF87188BBF18B9A75B4E27DF81BD6BC9CC1B7582A09D74BC3C88".parse().unwrap();
println!("Public key: {:?}", public_key.0);

Add the above code snippet to the top of a test case and run the test cases again to output the public key in bytes.

$cargo test -- --nocapture

The output should look similar to:

$public_key: [135, 40, 213, 241, 57, 171, 239, 135, 24, 139, 191, 24, 185, 167, 91, 78, 39, 223, 129, 189, 107, 201, 204, 27, 117, 130, 160, 157, 116, 188, 60, 136]

You can create the below public key constant. This public key can be used in test cases to check if your signature verification logic in the smart contract works.

const PUBLIC_KEY: PublicKeyEd25519 = PublicKeyEd25519([135, 40, 213, 241, 57, 171, 239, 135, 24, 139, 191, 24, 185, 167, 91, 78, 39, 223, 129, 189, 107, 201, 204, 27, 117, 130, 160, 157, 116, 188, 60, 136]);

previous

A Sponsored Transactions dApp

next

Sponsored Transactions Frontend and Backend

Copyright 2021 - 2023, Concordium Software ApS

This website only aggregates and analyzes the actions you take here if you allow cookies. If you do not allow cookies, it protects your privacy, but also prevents the owner from learning from your actions and creating a better experience for you and other users.

Note that if you opt in and you clear your cookies, delete the opt-in cookie, or if you change computers or Web browsers, you will need to perform the opt-in procedure again.

Privacy policy