diff --git a/bridge-token-factory/src/lib.rs b/bridge-token-factory/src/lib.rs index e2c30150..6df07c61 100644 --- a/bridge-token-factory/src/lib.rs +++ b/bridge-token-factory/src/lib.rs @@ -54,6 +54,9 @@ const SET_METADATA_GAS: Gas = Gas(Gas::ONE_TERA.0 * 5); /// Amount of gas used by bridge token to pause withdraw. const SET_PAUSED_GAS: Gas = Gas(Gas::ONE_TERA.0 * 5); +// Amount of gas used by bridge token to set new controller +const SET_CONTROLLER_GAS: Gas = Gas(Gas::ONE_TERA.0 * 5); + /// Amount of gas used by `upgrade_bridge_token` in the factory, without taking into account /// the gas consumed by the promise. const OUTER_UPGRADE_TOKEN_GAS: Gas = Gas(Gas::ONE_TERA.0 * 15); @@ -155,7 +158,12 @@ pub trait ExtBridgeTokenFactory { #[ext_contract(ext_bridge_token)] pub trait ExtBridgeToken { - fn mint(&self, account_id: AccountId, amount: U128); + fn mint( + &self, + account_id: AccountId, + amount: U128, + msg: Option, + ) -> PromiseOrValue; fn ft_transfer_call( &mut self, @@ -178,6 +186,8 @@ pub trait ExtBridgeToken { fn set_paused(&mut self, paused: bool); fn attach_full_access_key(&mut self, public_key: PublicKey) -> Promise; + + fn set_new_controller(&mut self, new_controller: Option); } pub fn assert_self() { @@ -399,22 +409,10 @@ impl BridgeTokenFactory { target, message )); - match message { - Some(message) => ext_bridge_token::ext(self.get_bridge_token_account_id(token.clone())) - .with_static_gas(MINT_GAS) - .with_attached_deposit(env::attached_deposit() - required_deposit) - .mint(env::current_account_id(), amount.into()) - .then( - ext_bridge_token::ext(self.get_bridge_token_account_id(token)) - .with_static_gas(FT_TRANSFER_CALL_GAS) - .with_attached_deposit(1) - .ft_transfer_call(target, amount.into(), None, message), - ), - None => ext_bridge_token::ext(self.get_bridge_token_account_id(token)) - .with_static_gas(MINT_GAS) - .with_attached_deposit(env::attached_deposit() - required_deposit) - .mint(target, amount.into()), - } + ext_bridge_token::ext(self.get_bridge_token_account_id(token)) + .with_static_gas(MINT_GAS + FT_TRANSFER_CALL_GAS) + .with_attached_deposit(env::attached_deposit() - required_deposit) + .mint(target, amount.into(), message) } /// Burn given amount of tokens and unlock it on the Ethereum side for the recipient address. @@ -422,8 +420,10 @@ impl BridgeTokenFactory { /// processing on Solidity side. /// Caller must be ., where exists in the `tokens`. #[result_serializer(borsh)] + #[allow(unused_variables)] pub fn finish_withdraw( &mut self, + #[serializer(borsh)] sender_id: AccountId, #[serializer(borsh)] amount: Balance, #[serializer(borsh)] recipient: String, ) -> ResultType { @@ -508,6 +508,26 @@ impl BridgeTokenFactory { ) } + /// Propose new controller for the provided tokens + /// + /// # Arguments + /// + /// * `tokens_account_id`: A list of tokens that need their controller updated. + /// * `new_controller`: New controller for tokens + /// + #[access_control_any(roles(Role::DAO))] + pub fn propose_new_controller_for_tokens( + &self, + tokens_account_id: Vec, + new_controller: AccountId, + ) { + for token_account_id in tokens_account_id { + ext_bridge_token::ext(token_account_id) + .with_static_gas(SET_CONTROLLER_GAS) + .set_new_controller(Some(new_controller.clone())); + } + } + /// Pause the withdraw method in the bridge token contract. /// /// # Arguments diff --git a/bridge-token/src/lib.rs b/bridge-token/src/lib.rs index d242bc5e..4972a251 100644 --- a/bridge-token/src/lib.rs +++ b/bridge-token/src/lib.rs @@ -14,7 +14,7 @@ use near_sdk::{ const FINISH_WITHDRAW_GAS: Gas = Gas(Gas::ONE_TERA.0 * 50); const OUTER_UPGRADE_GAS: Gas = Gas(Gas::ONE_TERA.0 * 15); const NO_DEPOSIT: Balance = 0; -const CURRENT_STATE_VERSION: u32 = 1; +const CURRENT_STATE_VERSION: u32 = 2; pub type Mask = u128; @@ -30,6 +30,7 @@ pub struct BridgeToken { decimals: u8, paused: Mask, icon: Option, + new_controller: Option } #[ext_contract(ext_bridge_token_factory)] @@ -37,6 +38,7 @@ pub trait ExtBridgeTokenFactory { #[result_serializer(borsh)] fn finish_withdraw( &self, + #[serializer(borsh)] sender_id: AccountId, #[serializer(borsh)] amount: Balance, #[serializer(borsh)] recipient: String, ) -> Promise; @@ -67,6 +69,7 @@ impl BridgeToken { decimals: 0, paused: Mask::default(), icon: None, + new_controller: None, } } @@ -91,7 +94,10 @@ impl BridgeToken { } #[payable] - pub fn mint(&mut self, account_id: AccountId, amount: U128) { + pub fn mint(&mut self, + account_id: AccountId, + amount: U128, + msg: Option) -> PromiseOrValue { assert_eq!( env::predecessor_account_id(), self.controller, @@ -99,7 +105,26 @@ impl BridgeToken { ); self.storage_deposit(Some(account_id.clone()), None); - self.token.internal_deposit(&account_id, amount.into()); + if let Some(msg) = msg { + self.token + .internal_deposit(&env::predecessor_account_id(), amount.into()); + + self.ft_transfer_call(account_id, amount, None, msg) + } else { + self.token.internal_deposit(&account_id, amount.into()); + PromiseOrValue::Value(amount) + } + } + + pub fn burn(&mut self, amount: U128) { + assert_eq!( + env::predecessor_account_id(), + self.controller, + "Only controller can call burn" + ); + + self.token + .internal_withdraw(&env::predecessor_account_id(), amount.into()); } #[payable] @@ -113,7 +138,18 @@ impl BridgeToken { ext_bridge_token_factory::ext(self.controller.clone()) .with_static_gas(FINISH_WITHDRAW_GAS) - .finish_withdraw(amount.into(), recipient) + .finish_withdraw(env::predecessor_account_id(), amount.into(), recipient) + } + + pub fn set_new_controller(&mut self, new_controller: Option) { + require!(self.controller_or_self()); + self.new_controller = new_controller; + } + + pub fn update_controller(&mut self) { + require!(Some(env::predecessor_account_id()) == self.new_controller); + self.controller = env::predecessor_account_id(); + self.new_controller = None; } pub fn account_storage_usage(&self) -> StorageUsage { @@ -204,6 +240,20 @@ pub struct BridgeTokenV0 { paused: Mask, } +#[near_bindgen] +#[derive(BorshDeserialize, BorshSerialize)] +pub struct BridgeTokenV1 { + controller: AccountId, + token: FungibleToken, + name: String, + symbol: String, + reference: String, + reference_hash: Base64VecU8, + decimals: u8, + paused: Mask, + icon: Option, +} + impl From for BridgeToken { fn from(obj: BridgeTokenV0) -> Self { #[allow(deprecated)] @@ -217,6 +267,25 @@ impl From for BridgeToken { decimals: obj.decimals, paused: obj.paused, icon: None, + new_controller: None, + } + } +} + +impl From for BridgeToken { + fn from(obj: BridgeTokenV1) -> Self { + #[allow(deprecated)] + Self { + controller: obj.controller, + token: obj.token, + name: obj.name, + symbol: obj.symbol, + reference: obj.reference, + reference_hash: obj.reference_hash, + decimals: obj.decimals, + paused: obj.paused, + icon: obj.icon, + new_controller: None, } } } @@ -232,6 +301,11 @@ impl BridgeToken { let new_state: BridgeToken = old_state.into(); assert!(new_state.controller_or_self()); new_state + } else if from_version == 1 { + let old_state: BridgeTokenV1 = env::state_read().expect("Contract isn't initialized"); + let new_state: BridgeToken = old_state.into(); + assert!(new_state.controller_or_self()); + new_state } else { env::state_read().unwrap() } diff --git a/res/bridge_token.wasm b/res/bridge_token.wasm index ae5a1ab0..81b1d113 100755 Binary files a/res/bridge_token.wasm and b/res/bridge_token.wasm differ diff --git a/res/bridge_token_factory.wasm b/res/bridge_token_factory.wasm index a593e00b..cfc7a138 100755 Binary files a/res/bridge_token_factory.wasm and b/res/bridge_token_factory.wasm differ diff --git a/res/rainbow_bridge_near_token_locker.wasm b/res/rainbow_bridge_near_token_locker.wasm index a2fdd0e3..38751bd7 100755 Binary files a/res/rainbow_bridge_near_token_locker.wasm and b/res/rainbow_bridge_near_token_locker.wasm differ