From ed76ea2954713859a0b97f05c7a72ac8e4c208db Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Mon, 27 May 2024 04:02:51 -0400 Subject: [PATCH] refactor(gas-service)!: support generic pay_gas method --- .../sources/gas_service/gas_service.move | 204 ++++++++++++++++-- 1 file changed, 181 insertions(+), 23 deletions(-) diff --git a/move/gas_service/sources/gas_service/gas_service.move b/move/gas_service/sources/gas_service/gas_service.move index 887eb7b6..699b8f39 100644 --- a/move/gas_service/sources/gas_service/gas_service.move +++ b/move/gas_service/sources/gas_service/gas_service.move @@ -8,6 +8,10 @@ module gas_service::gas_service { use sui::hash::keccak256; use sui::event; + // ----- + // Types + // ----- + public struct GasService has key, store { id: UID, balance: Balance, @@ -17,34 +21,42 @@ module gas_service::gas_service { id: UID, } - public struct NativeGasPaidForContractCall has copy, drop { + // ------ + // Events + // ------ + + public struct GasPaid has copy, drop { sender: address, destination_chain: String, destination_address: String, payload_hash: address, value: u64, refund_address: address, + params: vector, } - public struct NativeGasAdded has copy, drop { - tx_hash: address, - log_index: u64, + public struct GasAdded has copy, drop { + message_id: String, value: u64, refund_address: address, + params: vector, } - public struct Refunded has copy, drop { - tx_hash: address, - log_index: u64, + public struct Refunded has copy, drop { + message_id: String, value: u64, refund_address: address, } - public struct GasCollected has copy, drop { + public struct GasCollected has copy, drop { receiver: address, value: u64, } + // ----- + // Setup + // ----- + fun init(ctx: &mut TxContext) { transfer::share_object(GasService { id: object::new(ctx), @@ -56,8 +68,14 @@ module gas_service::gas_service { }, ctx.sender()); } - // This can only be called once since it needs its own upgrade cap which it deletes. - public fun pay_native_gas_for_contract_call( + // ---------------- + // Public Functions + // ---------------- + + /// Pay gas for a contract call. + /// This function is called by the channel that wants to pay gas for a contract call. + /// It can also be called by the user to pay gas for a contract call, while setting the sender as the channel ID. + public fun pay_gas( self: &mut GasService, coin: Coin, sender: address, @@ -65,34 +83,40 @@ module gas_service::gas_service { destination_address: String, payload: vector, refund_address: address, + params: vector, ) { let value = coin.value(); coin::put(&mut self.balance, coin); let payload_hash = address::from_bytes(keccak256(&payload)); - event::emit( NativeGasPaidForContractCall { + + event::emit(GasPaid { sender, destination_chain, destination_address, payload_hash, value, refund_address, + params, }) } - public fun add_native_gas( + /// Add gas for an existing cross-chain contract call. + /// This function can be called by a user who wants to add gas for a contract call with insufficient gas. + public fun add_gas( self: &mut GasService, coin: Coin, - tx_hash: address, - log_index: u64, - refund_address: address + message_id: String, + refund_address: address, + params: vector, ) { let value = coin.value(); coin::put(&mut self.balance, coin); - event::emit( NativeGasAdded { - tx_hash, - log_index, + + event::emit(GasAdded { + message_id, value, refund_address, + params, }); } @@ -102,23 +126,157 @@ module gas_service::gas_service { receiver, ); - event::emit(GasCollected { + event::emit(GasCollected { receiver, value: amount, }); } - public fun refund(self: &mut GasService, _: &GasCollectorCap, tx_hash: address, log_index: u64, receiver: address, amount: u64, ctx: &mut TxContext) { + public fun refund(self: &mut GasService, _: &GasCollectorCap, message_id: String, receiver: address, amount: u64, ctx: &mut TxContext) { transfer::public_transfer( coin::take(&mut self.balance, amount, ctx), receiver, ); - event::emit(Refunded { - tx_hash, - log_index, + event::emit(Refunded { + message_id, value: amount, refund_address: receiver, }); } + + // ----- + // Tests + // ----- + + #[test_only] + fun new(ctx: &mut TxContext): (GasService, GasCollectorCap) { + let service = GasService { + id: object::new(ctx), + balance: balance::zero(), + }; + + let cap = GasCollectorCap { + id: object::new(ctx), + }; + + (service, cap) + } + + #[test_only] + fun destroy(self: GasService) { + let GasService { id, balance } = self; + id.delete(); + balance.destroy_for_testing(); + } + + #[test_only] + fun destroy_cap(self: GasCollectorCap) { + let GasCollectorCap { id } = self; + id.delete(); + } + + #[test] + fun test_init() { + let ctx = &mut sui::tx_context::dummy(); + init(ctx); + } + + #[test] + fun test_pay_gas() { + let ctx = &mut sui::tx_context::dummy(); + let (mut service, cap) = new(ctx); + let value = 10; + let c: Coin = coin::mint_for_testing(value, ctx); + + service.pay_gas( + c, + ctx.sender(), + std::ascii::string(b"destination chain"), + std::ascii::string(b"destination address"), + vector[], + ctx.sender(), + vector[], + ); + + assert!(service.balance.value() == value, 0); + + cap.destroy_cap(); + service.destroy(); + } + + #[test] + fun test_add_gas() { + let ctx = &mut sui::tx_context::dummy(); + let (mut service, cap) = new(ctx); + let value = 10; + let c: Coin = coin::mint_for_testing(value, ctx); + + service.add_gas( + c, + std::ascii::string(b"message id"), + @0x0, + vector[], + ); + + assert!(service.balance.value() == value, 0); + + cap.destroy_cap(); + service.destroy(); + } + + #[test] + fun test_collect_gas() { + let ctx = &mut sui::tx_context::dummy(); + let (mut service, cap) = new(ctx); + let value = 10; + let c: Coin = coin::mint_for_testing(value, ctx); + + service.add_gas( + c, + std::ascii::string(b"message id"), + @0x0, + vector[], + ); + + service.collect_gas( + &cap, + ctx.sender(), + value, + ctx, + ); + + assert!(service.balance.value() == 0, 0); + + cap.destroy_cap(); + service.destroy(); + } + + #[test] + fun test_refund() { + let ctx = &mut sui::tx_context::dummy(); + let (mut service, cap) = new(ctx); + let value = 10; + let c: Coin = coin::mint_for_testing(value, ctx); + + service.add_gas( + c, + std::ascii::string(b"message id"), + @0x0, + vector[], + ); + + service.refund( + &cap, + std::ascii::string(b"message id"), + ctx.sender(), + value, + ctx, + ); + + assert!(service.balance.value() == 0, 0); + + cap.destroy_cap(); + service.destroy(); + } }