Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(interchain-token): update interchain token #71

Merged
merged 43 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
edba50f
feat(its): update interchain token
ahramy Nov 15, 2024
32063ae
Update utils.rs
ahramy Nov 15, 2024
6ceeee3
updated token meta data
ahramy Nov 15, 2024
637f115
Merge branch 'main' into feat/interchain-token
ahramy Nov 18, 2024
e110a1b
updated
ahramy Nov 19, 2024
5bca181
update
ahramy Nov 19, 2024
19b0854
Update contract.rs
ahramy Nov 19, 2024
52f28d8
Update contract.rs
ahramy Nov 19, 2024
58de543
Merge branch 'main' into feat/interchain-token
ahramy Nov 19, 2024
3567446
Merge branch 'main' into feat/interchain-token
ahramy Nov 20, 2024
278ec46
Update test.rs
ahramy Nov 21, 2024
2021973
feat(its): update interchain token
ahramy Nov 15, 2024
11d592b
updated
ahramy Nov 19, 2024
200331d
Update test.rs
ahramy Nov 21, 2024
c032e4a
Merge branch 'feat/interchain-token' of https://github.com/axelarnetw…
ahramy Nov 21, 2024
6ce90b2
Merge branch 'main' into feat/interchain-token
ahramy Nov 21, 2024
e63d039
Merge branch 'main' into feat/interchain-token
ahramy Nov 24, 2024
bf84196
Merge branch 'main' into feat/interchain-token
ahramy Nov 26, 2024
03a046e
Merge branch 'main' into feat/interchain-token
ahramy Nov 27, 2024
1b1f3af
feat(its): update interchain token
ahramy Nov 15, 2024
8b4a691
updated
ahramy Nov 19, 2024
5ab84af
Update test.rs
ahramy Nov 21, 2024
761749a
feat(its): update interchain token
ahramy Nov 15, 2024
40faa5b
updated token meta data
ahramy Nov 15, 2024
4624744
updated
ahramy Nov 19, 2024
6e27960
update
ahramy Nov 19, 2024
dd7f4cd
Update contract.rs
ahramy Nov 19, 2024
f9d4a34
Update contract.rs
ahramy Nov 19, 2024
0449388
Update test.rs
ahramy Nov 21, 2024
bd6f698
update
ahramy Nov 28, 2024
d4dd815
update
ahramy Nov 28, 2024
8865117
Merge branch 'main' into feat/interchain-token
ahramy Dec 1, 2024
c2ff702
update test
ahramy Dec 1, 2024
ac7e3f5
update
ahramy Dec 1, 2024
307a1ce
addressed comments
ahramy Dec 2, 2024
c833933
Update contract.rs
ahramy Dec 2, 2024
09339cb
Update contract.rs
ahramy Dec 2, 2024
e8592f6
update persistent to instance
ahramy Dec 2, 2024
3516525
updated
ahramy Dec 2, 2024
c210426
Update test.rs
ahramy Dec 2, 2024
6a920a5
Merge branch 'main' into feat/interchain-token
ahramy Dec 3, 2024
32b59d2
added lib, error handling, updated test
ahramy Dec 3, 2024
b84824d
Update test.rs
ahramy Dec 3, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ rust-version = "1.81.0"

[workspace.dependencies]
soroban-sdk = { version = "22.0.0-rc.3" }
soroban-token-sdk = { version = "22.0.0-rc.3" }
axelar-soroban-std = { version = "^0.1.0", path = "packages/axelar-soroban-std" }
axelar-gas-service = { version = "^0.1.0", path = "contracts/axelar-gas-service" }
ahramy marked this conversation as resolved.
Show resolved Hide resolved
axelar-gateway = { version = "^0.1.0", path = "contracts/axelar-gateway" }
Expand Down
1 change: 1 addition & 0 deletions contracts/interchain-token/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"]
[dependencies]
axelar-soroban-std = { workspace = true }
soroban-sdk = { workspace = true }
soroban-token-sdk = { workspace = true }

[dev-dependencies]
axelar-soroban-std = { workspace = true, features = ["testutils"] }
Expand Down
192 changes: 181 additions & 11 deletions contracts/interchain-token/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
use axelar_soroban_std::ensure;
use soroban_sdk::{contract, contractimpl, Address, Env};
use soroban_sdk::token::{self, Interface as _};
use soroban_sdk::{contract, contractimpl, Address, Bytes, Env, IntoVal, String};
use soroban_token_sdk::metadata::TokenMetadata;
use soroban_token_sdk::TokenUtils;

use crate::error::ContractError;
use crate::event;
use crate::storage_types::DataKey;
use crate::utils::{
admin, check_nonnegative_amount, extend_instance_ttl, read_allowance, read_balance,
read_decimal, read_name, read_symbol, receive_balance, spend_allowance, spend_balance,
write_allowance, write_metadata,
};

#[contract]
pub struct InterchainToken;

#[contractimpl]
impl InterchainToken {
pub fn initialize(env: Env, owner: Address) -> Result<(), ContractError> {
pub fn initialize_interchain_token(
ahramy marked this conversation as resolved.
Show resolved Hide resolved
env: Env,
interchain_token_service: Address,
admin: Address,
minter: Address,
token_id: Bytes,
ahramy marked this conversation as resolved.
Show resolved Hide resolved
token_meta_data: TokenMetadata,
) -> Result<(), ContractError> {
ensure!(!token_id.is_empty(), ContractError::TokenIdZero);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
ensure!(token_meta_data.decimal <= 18, ContractError::InvalidDecimal);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
ensure!(
!token_meta_data.name.is_empty(),
ContractError::TokenNameEmpty
);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
ensure!(
!token_meta_data.symbol.is_empty(),
ContractError::TokenSymbolEmpty
);

ensure!(
env.storage()
.instance()
Expand All @@ -20,27 +46,171 @@ impl InterchainToken {
);

env.storage().instance().set(&DataKey::Initialized, &true);
env.storage().instance().set(&DataKey::TokenId, &token_id);
write_metadata(&env, token_meta_data);

env.storage().instance().set(&DataKey::Owner, &owner);
env.storage().instance().set(&DataKey::Admin, &admin);
env.storage()
.persistent()
.set(&DataKey::Minter(minter), &true);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
env.storage()
.persistent()
.set(&DataKey::Minter(interchain_token_service.clone()), &true);

env.storage().instance().set(
&DataKey::InterchainTokenServiceAddress,
ahramy marked this conversation as resolved.
Show resolved Hide resolved
&interchain_token_service,
);

Ok(())
}

pub fn owner(env: &Env) -> Result<Address, ContractError> {
pub fn token_id(env: &Env) -> Address {
env.storage().instance().get(&DataKey::TokenId).unwrap()
ahramy marked this conversation as resolved.
Show resolved Hide resolved
}

pub fn interchain_token_service(env: &Env) -> Address {
env.storage()
.instance()
.get(&DataKey::Owner)
.ok_or(ContractError::NotInitialized)
.get(&DataKey::InterchainTokenServiceAddress)
.unwrap()
}

pub fn transfer_ownership(env: Env, new_owner: Address) -> Result<(), ContractError> {
let owner: Address = Self::owner(&env)?;
owner.require_auth();
pub fn mint(env: Env, minter: Address, to: Address, amount: i128) -> Result<(), ContractError> {
minter.require_auth_for_args((&to, amount).into_val(&env));
ahramy marked this conversation as resolved.
Show resolved Hide resolved

check_nonnegative_amount(amount);
ahramy marked this conversation as resolved.
Show resolved Hide resolved

env.storage().instance().set(&DataKey::Owner, &new_owner);
let admin = admin(&env);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
if admin != minter {
ahramy marked this conversation as resolved.
Show resolved Hide resolved
let is_authorized = env
.storage()
.persistent()
.get::<_, bool>(&DataKey::Minter(minter))
.unwrap_or(false);

event::transfer_ownership(&env, owner, new_owner);
if !is_authorized {
return Err(ContractError::NotAuthorizedMinter);
}
}

extend_instance_ttl(&env);
ahramy marked this conversation as resolved.
Show resolved Hide resolved

receive_balance(&env, to.clone(), amount);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
TokenUtils::new(&env).events().mint(admin, to, amount);

Ok(())
}

pub fn set_admin(env: Env, new_admin: Address) {
ahramy marked this conversation as resolved.
Show resolved Hide resolved
let admin = admin(&env);
admin.require_auth();

extend_instance_ttl(&env);

env.storage().instance().set(&DataKey::Admin, &admin);
TokenUtils::new(&env)
.events()
.set_admin(admin.clone(), new_admin.clone());

event::set_admin(&env, admin, new_admin);
}

pub fn add_minter(env: &Env, minter: Address) {
ahramy marked this conversation as resolved.
Show resolved Hide resolved
admin(env).require_auth();

env.storage()
.persistent()
.set(&DataKey::Minter(minter.clone()), &true);

event::add_minter(env, minter);
}
}

#[contractimpl]
impl token::Interface for InterchainToken {
fn allowance(env: Env, from: Address, spender: Address) -> i128 {
extend_instance_ttl(&env);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
read_allowance(&env, from, spender).amount
}

fn approve(env: Env, from: Address, spender: Address, amount: i128, expiration_ledger: u32) {
from.require_auth();

check_nonnegative_amount(amount);
extend_instance_ttl(&env);

write_allowance(
&env,
from.clone(),
spender.clone(),
amount,
expiration_ledger,
);

TokenUtils::new(&env)
.events()
.approve(from, spender, amount, expiration_ledger);
}

fn balance(env: Env, id: Address) -> i128 {
extend_instance_ttl(&env);
read_balance(&env, id)
}

fn transfer(env: Env, from: Address, to: Address, amount: i128) {
from.require_auth();

check_nonnegative_amount(amount);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
extend_instance_ttl(&env);

spend_balance(&env, from.clone(), amount);
receive_balance(&env, to.clone(), amount);
TokenUtils::new(&env).events().transfer(from, to, amount);
ahramy marked this conversation as resolved.
Show resolved Hide resolved
}

fn transfer_from(env: Env, spender: Address, from: Address, to: Address, amount: i128) {
spender.require_auth();

check_nonnegative_amount(amount);
extend_instance_ttl(&env);

spend_allowance(&env, from.clone(), spender, amount);
spend_balance(&env, from.clone(), amount);
receive_balance(&env, to.clone(), amount);
TokenUtils::new(&env).events().transfer(from, to, amount)
ahramy marked this conversation as resolved.
Show resolved Hide resolved
}

fn burn(env: Env, from: Address, amount: i128) {
from.require_auth();

check_nonnegative_amount(amount);
extend_instance_ttl(&env);

spend_balance(&env, from.clone(), amount);
TokenUtils::new(&env).events().burn(from, amount);
}

fn burn_from(env: Env, spender: Address, from: Address, amount: i128) {
ahramy marked this conversation as resolved.
Show resolved Hide resolved
spender.require_auth();

check_nonnegative_amount(amount);
extend_instance_ttl(&env);

spend_allowance(&env, from.clone(), spender, amount);
spend_balance(&env, from.clone(), amount);
TokenUtils::new(&env).events().burn(from, amount)
}

fn decimals(env: Env) -> u32 {
ahramy marked this conversation as resolved.
Show resolved Hide resolved
read_decimal(&env)
}

fn name(env: Env) -> String {
read_name(&env)
}

fn symbol(env: Env) -> String {
read_symbol(&env)
}
}
8 changes: 6 additions & 2 deletions contracts/interchain-token/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use soroban_sdk::contracterror;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u32)]
pub enum ContractError {
NotInitialized = 1,
AlreadyInitialized = 2,
AlreadyInitialized = 1,
InvalidDecimal = 2,
TokenIdZero = 3,
TokenNameEmpty = 4,
TokenSymbolEmpty = 5,
NotAuthorizedMinter = 6,
}
13 changes: 7 additions & 6 deletions contracts/interchain-token/src/event.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use soroban_sdk::{Address, Env, Symbol};

pub fn transfer_ownership(env: &Env, previous_owner: Address, new_owner: Address) {
let topics = (
Symbol::new(env, "ownership_transferred"),
previous_owner,
new_owner,
);
pub fn set_admin(env: &Env, previous_admin: Address, new_admin: Address) {
ahramy marked this conversation as resolved.
Show resolved Hide resolved
let topics = (Symbol::new(env, "set_admin"), previous_admin, new_admin);
env.events().publish(topics, ());
}

pub fn add_minter(env: &Env, minter: Address) {
let topics = (Symbol::new(env, "add_minter"), minter);
env.events().publish(topics, ());
}
9 changes: 7 additions & 2 deletions contracts/interchain-token/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
#![no_std]
ahramy marked this conversation as resolved.
Show resolved Hide resolved

pub mod contract;
pub mod error;
mod event;
mod storage_types;
mod utils;

pub mod contract;
// Allows using std (and its macros) in test modules
#[cfg(test)]
#[macro_use]
extern crate std;

pub use contract::InterchainTokenClient;
pub use crate::contract::InterchainTokenClient;
32 changes: 29 additions & 3 deletions contracts/interchain-token/src/storage_types.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
use soroban_sdk::contracttype;
use soroban_sdk::{contracttype, Address};

pub const DAY_IN_LEDGERS: u32 = 17280;
pub const INSTANCE_BUMP_AMOUNT: u32 = 7 * DAY_IN_LEDGERS;
ahramy marked this conversation as resolved.
Show resolved Hide resolved
pub const INSTANCE_LIFETIME_THRESHOLD: u32 = INSTANCE_BUMP_AMOUNT - DAY_IN_LEDGERS;

ahramy marked this conversation as resolved.
Show resolved Hide resolved
pub const BALANCE_BUMP_AMOUNT: u32 = 30 * DAY_IN_LEDGERS;
pub const BALANCE_LIFETIME_THRESHOLD: u32 = BALANCE_BUMP_AMOUNT - DAY_IN_LEDGERS;

#[derive(Clone)]
#[contracttype]
pub struct AllowanceDataKey {
pub from: Address,
pub spender: Address,
}

#[contracttype]
pub struct AllowanceValue {
pub amount: i128,
pub expiration_ledger: u32,
}

#[contracttype]
#[derive(Clone, Debug)]
#[derive(Clone)]
pub enum DataKey {
Initialized,
Owner,
Allowance(AllowanceDataKey),
Balance(Address),
State(Address),
Admin,
Minter(Address),
ahramy marked this conversation as resolved.
Show resolved Hide resolved
TokenId,
InterchainTokenServiceAddress,
}
Loading
Loading