Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/squid-changes
Browse files Browse the repository at this point in the history
  • Loading branch information
Foivos committed May 15, 2024
2 parents c9373fa + d073303 commit b3a0d0e
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 51 deletions.
66 changes: 59 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,43 @@
# Axelar CGP Sui

An implementation of the Axelar gateway for the Sui blockchain.
An implementation of the Axelar cross-chain contracts in Move for the Sui blockchain.

## Installation

Install Sui and run a local Sui network: https://docs.sui.io/build/sui-local-network.
The copy `.env.exaple` to `.env` and add a valid private key for sui. If you want to deploy to testnet you shoukd have its address be funded.
Install Sui as shown [here](https://docs.sui.io/guides/developer/getting-started/sui-install)

## Deployment
Install node.js 18+

Build all Move packages

```sh
npm ci

To deploy to testnet run `node scripts/publish-package axelar testnet`
npm run build
```

## Testing

run `npm run test` to run move tests on all the move packages.
Run tests for all Move packages

```sh
npm run test
```

### Development

Install the `Move` extension in VS Code. It should come pre-installed with `move-analyzer`.

Move Book: https://move-book.com
Move Examples: https://examples.sui.io

## Deployment

Copy `.env.example` to `.env` and add a valid private key for Sui. If you want to deploy to testnet the corresponding address should be funded.

Run a [local](https://docs.sui.io/build/sui-local-network) Sui network for local testing, or use testnet.

To deploy to testnet run `node scripts/publish-package axelar testnet`

## Scripts

Expand All @@ -23,10 +47,37 @@ First run `node scripts/publish-package` and then any of `node scripts/<testScri

The gateway lives in a few modules but has all of its storage in a single shared object called `AxelarValidators`. This is not very aptly named and we might rename it.

## Relayer Spec

Relaying to Sui is a bit complicated: the concept of 'smart contracts' is quite warped compared to EVM chains.

### The Problems

Firstly, all persistent storage stems from `Objects` that have the `key` property and are either shared or owned by an account. These objects have a specific `type`, which is defined in a certain module, and only that module can access their storage (either to read or to write to it). This means that instead of calling a smart contract with some data, in Sui one needs to call a module, pass the Objects that are to be modified as arguments, alongside extra arguments that specify the kind of change that should occur. This additionally means that the concept of `msg.sender` does not apply to applications, and a certain `capability` object that functions like a 'key' to unlock certain functionality needs to be used instead.

The second issue is the lack of interfaces whatsoever. It is impossible for a module to ever call a module that is published at a later time. This means applications that want to interface with future applications must be called by those future applications, but need to call pre-existing ones. To expand on this, we expect contract calls that are received to potentially modify the storage of multiple objects in a single call, which makes it impossible to require modules to implement a 'standardized' function that a relayer will call, because the number of arguments required varies depending on the application (or the type of call).

Finally, we do not want to require the payload of incoming calls to have a certain format, because that would mean that working applications that want to exapnd to Sui need to redesign their working protocoll to accomodate Sui, discouraging them from doing so.

### The Solutions

First of all, as we mentioned before, for applications to 'validate' themselves with other applications need to use a `capability` object. This object will be called `Channel`, and it will hold information about the executed contract calls as well. It has a field called `id` which specifies the 'address' of the application for the purposed of incoming and outgoing extenral calls. This `id` has to match the `id` of a shared object that is passed in the channel creation method (alongside a witness for security). This shared object can easily be querried by the relayer to get call fullfilment information. Specifically:

- The shared object has to have a field called `get_call_info_object_ids` that is a `vector<address>`.
- The module that defined the shared object type has to implement a function called `get_call_info`, which has no types, and takes the incoming call `payload` as the first argument, followed by a number of shared objects whose ids are specified by the `get_call_info_object_ids` mentioned above. This function has to return a `std::ascii::String` which is the JSON encoded call data to fullfill the contract call.
- This calldata has the following 3 fields:
- `target`: the target method, in the form of `packag_iId::module_name::function_name`.
- `arguments`: an array of arguments that can be:
- `contractCall`: the `ApprovedCall` object (see below).
- `pure:${info}`: a pure argument specified by `$info`.
- `obj:${objectId}`: a shared object with the specified `id`.
- `typeArguments`: a list of types to be passed to the function called

## ITS spec

The ITS on sui is supposed to be able to receive 3 messages:
- Register Coin: The payload will be abi encoded data that looks like this:

- Register Coin: The payload will be abi encoded data that looks like this:
`4`: `uint256`, fixed,
`tokenId`: `bytes32`, fixed,
`name`: `string`, variable,
Expand Down Expand Up @@ -62,6 +113,7 @@ ITS also needs to be able to send 2 calls, the call to receive coin and the call
This module and the object it creates (`CoinManagement<T>`) will tell if a coin is registered as a mint/burn or lock unlock token.

To create a `CoinManagement` object one has to call

- `mint_burn<T>`, passing in a `TreasuryCap<T>`.
- `lock_unlock<T>`.
- `lock_unlock_funded<T>` passing in some initial `Coin<T>` to lock.
Expand Down
6 changes: 3 additions & 3 deletions move/axelar/sources/utils.move
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ module axelar::utils {

public fun compare_address_vectors(v1: &vector<u8>, v2: &vector<u8>): bool {
let length = vector::length(v1);
assert!(length == vector::length(v2), EVectorLengthMismatch);
assert!(length == vector::length(v2), EVectorLengthMismatch);
let mut i = 0;
while(i < length) {
let b1 = *vector::borrow(v1, i);
Expand All @@ -76,7 +76,7 @@ module axelar::utils {
let prefix = x"5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962066d6f64756c650866756e6374696f6e02021234025678020574797065310574";
let mut signature = x"5f7809eb09754577387a816582ece609511d0262b2c52aa15306083ca3c85962066d6f64756c650866756e6374696f6e02021234025678020574797065310574";
let inputs = vector[0, 1, 10, 11, 27, 28, 30, 38, 39];
let outputs = vector[0, 1, 10, 11, 0, 1, 30, 1, 0];
let outputs = vector[0, 1, 10, 11, 0, 1, 30, 1, 0];

let length = vector::length(&inputs);
let mut i = 0;
Expand Down Expand Up @@ -104,7 +104,7 @@ module axelar::utils {
let weights = vector[1, 3, 6];
let threshold = 4;
let output = x"dd5d3f9c1017e8356ea1858db7b89800b6cd43775c5c1b7c633f6ef933583cfd";

assert!(operators_hash(&operators, &weights, threshold) == output, 0);
}

Expand Down
56 changes: 43 additions & 13 deletions move/gas_service/sources/gas_service/gas_service.move
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,50 @@ module gas_service::gas_service {
use sui::hash::keccak256;
use sui::event;

public struct GasService
has key, store {
public struct GasService has key, store {
id: UID,
balance: Balance<SUI>,
}

public struct RefunderCap has key, store {
public struct GasCollectorCap has key, store {
id: UID,
}

public struct NativeGasPaidForContractCall has copy, drop {
sender: address,
destination_chain: String,
destination_address: String,
payload_hash: address,
sender: address,
destination_chain: String,
destination_address: String,
payload_hash: address,
value: u64,
refund_address: address,
}

public struct NativeGasAdded has copy, drop {
tx_hash: address,
tx_hash: address,
log_index: u64,
value: u64,
value: u64,
refund_address: address,
}

public struct Refunded has copy, drop {
tx_hash: address,
log_index: u64,
value: u64,
refund_address: address,
}

public struct GasCollected has copy, drop {
receiver: address,
value: u64,
}

fun init(ctx: &mut TxContext) {
transfer::share_object(GasService {
id: object::new(ctx),
balance: balance::zero<SUI>(),
});

transfer::public_transfer(RefunderCap {
transfer::public_transfer(GasCollectorCap {
id: object::new(ctx),
}, ctx.sender());
}
Expand Down Expand Up @@ -85,10 +96,29 @@ module gas_service::gas_service {
});
}

public fun refund(self: &mut GasService, _: &RefunderCap, receiver: address, amount: u64, ctx: &mut TxContext) {
public fun collect_gas(self: &mut GasService, _: &GasCollectorCap, receiver: address, amount: u64, ctx: &mut TxContext) {
transfer::public_transfer(
coin::take(&mut self.balance, amount, ctx),
receiver,
)
);

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) {
transfer::public_transfer(
coin::take(&mut self.balance, amount, ctx),
receiver,
);

event::emit(Refunded {
tx_hash,
log_index,
value: amount,
refund_address: receiver,
});
}
}
}
40 changes: 14 additions & 26 deletions move/squid/sources/squid/deepbook_v2.move
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module squid::deepbook_v2 {
const FLOAT_SCALING: u64 = 1_000_000_000;

const SWAP_TYPE: u8 = 1;

const EWrongSwapType: u64 = 0;
const EWrongPool: u64 = 1;
const EWrongCoinType: u64 = 2;
Expand Down Expand Up @@ -158,17 +158,17 @@ module squid::deepbook_v2 {
while(i > 0) {
i = i - 1;
let (used, max_out) = get_max_quote_from_base(
pool,
*vector::borrow(&prices, i),
*vector::borrow(&depths, i),
pool,
*vector::borrow(&prices, i),
*vector::borrow(&depths, i),
amount_left
);
amount_left = amount_left - used;
output = output + max_out;
if(amount_left < lot_size) break;
};
(amount_left, output)

}

public fun predict_quote_for_base<T1, T2>(pool: &Pool<T1, T2>, amount: u64, lot_size: u64, clock: &Clock): (u64, u64) {
Expand All @@ -179,23 +179,23 @@ module squid::deepbook_v2 {
let mut output = 0;
let mut i = 0;
let length = vector::length(&prices);
while(i < length) {
let price = *vector::borrow(&prices, i);
while(i < length) {
let price = *vector::borrow(&prices, i);
let (used, max_out) = get_max_base_from_quote(
pool,
price,
*vector::borrow(&depths, i),
amount_left,
pool,
price,
*vector::borrow(&depths, i),
amount_left,
lot_size
);

amount_left = amount_left - used;
output = output + max_out;
let (_, left_base) = unsafe_div_round(amount_left, price);
if (left_base < lot_size) break;
i = i + 1;
};

(amount_left, output)
}

Expand Down Expand Up @@ -368,16 +368,4 @@ module squid::deepbook_v2 {
vector[type_base, type_quote] ,
)
}


#[test]
fun test() {
let ctx = &mut tx_context.dummy();
clob::create_pool(
100,
100,
coin,
ctx,
);
}
}
}
2 changes: 1 addition & 1 deletion move/squid/sources/squid/discovery.move
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,4 @@ module squid::discovery {
vector[],
)
}
}
}
2 changes: 1 addition & 1 deletion move/squid/sources/squid/squid.move
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ module squid::squid {
public(package) fun coin_bag(self: &mut Squid): &mut CoinBag{
&mut self.coin_bag
}
}
}

0 comments on commit b3a0d0e

Please sign in to comment.