Skip to main content

linera_sdk/abis/
wrapped_fungible.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! An ABI for applications that implement a wrapped (bridged) fungible token with Mint/Burn.
5
6use std::collections::BTreeMap;
7
8use async_graphql::{Request, Response};
9pub use linera_base::identifiers::Account;
10use linera_base::{
11    abi::{ContractAbi, ServiceAbi},
12    data_types::U128,
13    identifiers::{AccountOwner, ApplicationId, ChainId},
14};
15use linera_sdk_derive::{GraphQLMutationRootInCrate, StableEnumInCrate};
16use serde::{Deserialize, Serialize};
17
18/// Parameters for a wrapped fungible token backed by an EVM bridge.
19#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
20pub struct WrappedParameters {
21    /// Ticker symbol (e.g. "USDC")
22    pub ticker_symbol: String,
23    /// Number of decimal places used by the source ERC-20 (e.g. 6 for USDC).
24    pub decimals: u8,
25    /// The chain on which minting and burning are allowed (the bridge chain).
26    pub mint_chain_id: ChainId,
27    /// The ERC-20 token address on the source EVM chain
28    pub evm_token_address: [u8; 20],
29    /// The EVM chain ID of the source chain (e.g. 8453 for Base)
30    pub evm_source_chain_id: u64,
31}
32
33/// Event emitted by the bridge application on its "burns" stream when it burns
34/// wrapped tokens on the bridge chain. The relayer observes these and forwards
35/// them to EVM to release the corresponding ERC-20 tokens.
36#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
37pub struct BurnEvent {
38    /// The Ethereum address to receive the unlocked ERC-20 tokens
39    pub target: [u8; 20],
40    /// Amount of tokens burned, in raw sub-units of the source ERC-20.
41    pub amount: U128,
42}
43
44/// Initial accounts and balances for the wrapped fungible token application.
45#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
46pub struct InitialState {
47    pub accounts: BTreeMap<AccountOwner, U128>,
48}
49
50/// Builder for [`InitialState`].
51#[derive(Debug, Default)]
52pub struct InitialStateBuilder {
53    account_balances: BTreeMap<AccountOwner, U128>,
54}
55
56impl InitialStateBuilder {
57    pub fn with_account(mut self, account: AccountOwner, balance: U128) -> Self {
58        self.account_balances.insert(account, balance);
59        self
60    }
61
62    pub fn build(&self) -> InitialState {
63        InitialState {
64            accounts: self.account_balances.clone(),
65        }
66    }
67}
68
69/// Response variants returned by the wrapped fungible token application.
70#[derive(Debug, StableEnumInCrate, Default)]
71pub enum FungibleResponse {
72    #[default]
73    Ok,
74    Balance(U128),
75    TickerSymbol(String),
76}
77
78/// Operations for the wrapped fungible token application.
79#[derive(Debug, StableEnumInCrate, GraphQLMutationRootInCrate)]
80pub enum WrappedFungibleOperation {
81    /// Requests an account balance.
82    Balance { owner: AccountOwner },
83    /// Requests this fungible token's ticker symbol.
84    TickerSymbol,
85    /// Approve the transfer of tokens.
86    Approve {
87        owner: AccountOwner,
88        spender: AccountOwner,
89        allowance: U128,
90    },
91    /// Transfers tokens from a (locally owned) account to a (possibly remote) account.
92    Transfer {
93        owner: AccountOwner,
94        amount: U128,
95        target_account: Account,
96    },
97    /// Transfers tokens from a (locally owned) account using a previously approved allowance.
98    TransferFrom {
99        owner: AccountOwner,
100        spender: AccountOwner,
101        amount: U128,
102        target_account: Account,
103    },
104    /// Same as `Transfer` but the source account may be remote.
105    Claim {
106        source_account: Account,
107        amount: U128,
108        target_account: Account,
109    },
110    /// Mints new tokens and transfers them to a target account. Driven by the
111    /// registered authorized caller (see [`Self::RegisterAuthorizedCaller`]) on the
112    /// designated mint chain.
113    MintAndTransfer {
114        target_account: Account,
115        amount: U128,
116    },
117    /// Burns tokens from an account. Authorized only via the registered authorized
118    /// caller (see [`Self::RegisterAuthorizedCaller`]) on the designated mint chain.
119    Burn { owner: AccountOwner, amount: U128 },
120    /// Registers the application authorized to drive `MintAndTransfer`/`Burn`. Must run on
121    /// the designated `mint_chain_id` — the only chain where it is consulted — and
122    /// requires an authenticated signer. Because an authorized caller may take
123    /// this token's id as a creation parameter, the two cannot reference each
124    /// other at creation; this token is created first and registers its caller
125    /// afterwards.
126    RegisterAuthorizedCaller { app_id: ApplicationId },
127}
128
129/// Cross-chain message used by the wrapped fungible token application.
130/// Amounts are [`U128`] in the source ERC-20's decimal scale.
131#[derive(Debug, Deserialize, Serialize)]
132pub enum Message {
133    /// Credits the given `target` account, unless the message is bouncing, in which case
134    /// `source` is credited instead.
135    Credit {
136        target: AccountOwner,
137        amount: U128,
138        source: AccountOwner,
139    },
140
141    /// Withdraws from the given account and starts a transfer to the target account.
142    Withdraw {
143        owner: AccountOwner,
144        amount: U128,
145        target_account: Account,
146    },
147}
148
149/// ABI for the wrapped fungible token application.
150pub struct WrappedFungibleTokenAbi;
151
152impl ContractAbi for WrappedFungibleTokenAbi {
153    type Operation = WrappedFungibleOperation;
154    type Response = FungibleResponse;
155}
156
157impl ServiceAbi for WrappedFungibleTokenAbi {
158    type Query = Request;
159    type QueryResponse = Response;
160}