207 lines
8.9 KiB
Plaintext
207 lines
8.9 KiB
Plaintext
module nft_bridge::transfer {
|
|
use std::vector;
|
|
use wormhole::serialize::{
|
|
serialize_u8,
|
|
serialize_u16,
|
|
};
|
|
use wormhole::deserialize::{
|
|
deserialize_u8,
|
|
deserialize_u16,
|
|
};
|
|
use wormhole::cursor;
|
|
use wormhole::external_address::{Self, ExternalAddress};
|
|
use wormhole::u16::U16;
|
|
|
|
use nft_bridge::uri::{Self, URI};
|
|
|
|
use token_bridge::string32::{Self, String32};
|
|
|
|
friend nft_bridge::transfer_nft;
|
|
|
|
#[test_only]
|
|
friend nft_bridge::complete_transfer_test;
|
|
#[test_only]
|
|
friend nft_bridge::transfer_test;
|
|
#[test_only]
|
|
friend nft_bridge::wrapped_test;
|
|
|
|
const E_INVALID_ACTION: u64 = 0;
|
|
|
|
struct Transfer has drop {
|
|
/// Address of the token. Left-zero-padded if shorter than 32 bytes
|
|
token_address: ExternalAddress,
|
|
/// Chain ID of the token
|
|
token_chain: U16,
|
|
/// Symbol of the token
|
|
symbol: String32,
|
|
/// Name of the token
|
|
name: String32,
|
|
/// Token ID
|
|
token_id: ExternalAddress,
|
|
/// URI of the token metadata
|
|
uri: URI,
|
|
/// Address of the recipient. Left-zero-padded if shorter than 32 bytes
|
|
to: ExternalAddress,
|
|
/// Chain ID of the recipient
|
|
to_chain: U16,
|
|
}
|
|
|
|
public fun get_token_address(a: &Transfer): ExternalAddress {
|
|
a.token_address
|
|
}
|
|
|
|
public fun get_token_chain(a: &Transfer): U16 {
|
|
a.token_chain
|
|
}
|
|
|
|
public fun get_symbol(a: &Transfer): String32 {
|
|
a.symbol
|
|
}
|
|
|
|
public fun get_name(a: &Transfer): String32 {
|
|
a.name
|
|
}
|
|
|
|
public fun get_token_id(a: &Transfer): ExternalAddress {
|
|
a.token_id
|
|
}
|
|
|
|
public fun get_uri(a: &Transfer): URI {
|
|
a.uri
|
|
}
|
|
|
|
public fun get_to(a: &Transfer): ExternalAddress {
|
|
a.to
|
|
}
|
|
|
|
public fun get_to_chain(a: &Transfer): U16 {
|
|
a.to_chain
|
|
}
|
|
|
|
public(friend) fun create(
|
|
token_address: ExternalAddress,
|
|
token_chain: U16,
|
|
symbol: String32,
|
|
name: String32,
|
|
token_id: ExternalAddress,
|
|
uri: URI,
|
|
to: ExternalAddress,
|
|
to_chain: U16,
|
|
): Transfer {
|
|
Transfer {
|
|
token_address,
|
|
token_chain,
|
|
symbol,
|
|
name,
|
|
token_id,
|
|
uri,
|
|
to,
|
|
to_chain,
|
|
}
|
|
}
|
|
|
|
public fun parse(transfer: vector<u8>): Transfer {
|
|
let cur = cursor::init(transfer);
|
|
let action = deserialize_u8(&mut cur);
|
|
assert!(action == 1, E_INVALID_ACTION);
|
|
let token_address = external_address::deserialize(&mut cur);
|
|
let token_chain = deserialize_u16(&mut cur);
|
|
let symbol = string32::deserialize(&mut cur);
|
|
let name = string32::deserialize(&mut cur);
|
|
let token_id = external_address::deserialize(&mut cur);
|
|
let uri = uri::deserialize(&mut cur);
|
|
let to = external_address::deserialize(&mut cur);
|
|
let to_chain = deserialize_u16(&mut cur);
|
|
cursor::destroy_empty(cur);
|
|
Transfer {
|
|
token_address,
|
|
token_chain,
|
|
symbol,
|
|
name,
|
|
token_id,
|
|
uri,
|
|
to,
|
|
to_chain,
|
|
}
|
|
}
|
|
|
|
public fun encode(transfer: &Transfer): vector<u8> {
|
|
let encoded = vector::empty<u8>();
|
|
serialize_u8(&mut encoded, 1);
|
|
external_address::serialize(&mut encoded, transfer.token_address);
|
|
serialize_u16(&mut encoded, transfer.token_chain);
|
|
string32::serialize(&mut encoded, transfer.symbol);
|
|
string32::serialize(&mut encoded, transfer.name);
|
|
external_address::serialize(&mut encoded, transfer.token_id);
|
|
uri::serialize(&mut encoded, transfer.uri);
|
|
external_address::serialize(&mut encoded, transfer.to);
|
|
serialize_u16(&mut encoded, transfer.to_chain);
|
|
encoded
|
|
}
|
|
|
|
}
|
|
|
|
#[test_only]
|
|
module nft_bridge::transfer_test {
|
|
use nft_bridge::transfer;
|
|
use nft_bridge::uri;
|
|
use token_bridge::string32;
|
|
use wormhole::external_address;
|
|
use wormhole::u16;
|
|
|
|
// VAA from https://etherscan.io/tx/0x8250625a8dfb66ecc9d5b8fd188057ef332c0a1d09a1510131f7b104e8dbf79b
|
|
const SOLANA_NFT: vector<u8> = x"01000000010d004ee8f9a0a898aedc2340289880ef662b55333cb4a9a374282f494706a780815e5462aff9e2f59da0a7402486ad5a5ea4e5604c4c3270c269dde700ff63011a7600022f1cd8622698fdbe116477e164175243271dc5a47a649fbbfc5ab44d7c0c8efc1ec9df05f7543dea0f67faa84ac53953bf57dd7a674dfe124fbc941e5471d5100103b63085d5a9fdc531d6e04f907fd1bd80217016878bf44fea1f41f5408a1e62426bf64e115b732aa53154f199f4299a5b1a4110ec3dbc1d69f4f4e386489da98e0104e46ac1fc4c35b8835191c9ec8f7146a212b33ce8592f8311addbcbed9fdd3caa257cb43cd07de39cc76f17edee89fb424f5797b08a71ab8a913b1c899d0182f3000561d3ba8f0ea84da5ccdf916ab22ed2c55b5ab2a2d0bfb8d6e4487285bb1817fb272b74aba27822045ad1b5dfd4378e8aadfef290c1c13007f7bd2f4f611cf32800070b83d82dd6dc975c1bcaefb8acb0e8127015805940d89366bf1d86988650e809522a29bf1c30393cf1bf46d5f0beefbe01dde1f20807116148e8dd20d339c24b00090f44858974ad211f9d21d7d59eb1b10207f7c1ae4591d3eaaef86afcc7afbe926584e38b58fc5d441890b905897346f1a8f9fca48217509a0415f06f80586804010a1de1bf4416235e41d56d5634161eb8544f3d1c82a5ee666c13402ad8652e706112af89e902974b61c4be8d253c24eb858872cf80d556bc8288487fd18c9673f8010c104c3c79c2224fa5bd04ab2737487cc2b49f05096237d3678ccbe57b92791471473e735c09659083758702ba241f99d6550a02b11cc43ba407c2eb1a294eb491010d436bc9540cef59afa60764c5d43640d854ff847f3afce0844c7de085e0e45df34693871393e701d15a7a5385c4c394c394b2d6427b7f16b57c89bd8d1e83a721000e56baf9369bcef85e1b705a7e5904f576101ff28dddce6078cfb1ee9ce95170c70ff05e6a8ff500b3a44877894fec08fd16244764675127c2497200301e76bb13010f1e8b4335050134d419ff985ba3d26f734eb19e78a0a3b39739a2eb9993eeb62c19569ac040f535e6f9093366fce418f127fe548dff25cba8d3b9b65f24306cc80010ae2a582ba0435d155115b6bfe9c92b5f7c0147670bd4b6d815f9507f689f80f7495114c0e742c108dc982038be28e89bde5124306b6cc39cbb3fd4ed32c74783006149baf70000cb2800010def15a24423e1edd1a5ab16f557b9060303ddbab8c803d2ee48f4b78a1cfd6b000000000000000001010101010101010101010101010101010101010101010101010101010101010101000154535400000000000000000000000000000000000000000000000000000000005465737400000000000000000000000000000000000000000000000000000000c4a79ff9105f87c3ed880ed4252b61759251acc6050c163c0a5d828d92dc0cb8c868747470733a2f2f6172742d73616e64626f782e73756e666c6f7765722e696e64757374726965732f746f6b656e2f3078643538343334663333613230363631663138366666363736323665613662646634316238306263612f393630000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000096d13cbeffe7bae169b9032fe69ed56eb07b300f0002";
|
|
|
|
#[test]
|
|
public fun parse_sol_transfer() {
|
|
let vaa = wormhole::vaa::parse_test(SOLANA_NFT);
|
|
let parsed_transfer = transfer::parse(wormhole::vaa::destroy(vaa));
|
|
|
|
let token_address = external_address::from_bytes(x"0101010101010101010101010101010101010101010101010101010101010101");
|
|
let token_chain = u16::from_u64(1);
|
|
let symbol = string32::from_bytes(b"TST");
|
|
let name = string32::from_bytes(b"Test");
|
|
let to = external_address::from_bytes(x"00000000000000000000000096d13cbeffe7bae169b9032fe69ed56eb07b300f");
|
|
let token_id = external_address::from_bytes(x"c4a79ff9105f87c3ed880ed4252b61759251acc6050c163c0a5d828d92dc0cb8");
|
|
// The VAA uses all 200 characters and pads with a bunch of 0 bytes at the end
|
|
let uri = uri::from_bytes(b"https://art-sandbox.sunflower.industries/token/0xd58434f33a20661f186ff67626ea6bdf41b80bca/960\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
|
|
let to_chain = u16::from_u64(2);
|
|
let transfer = transfer::create(
|
|
token_address,
|
|
token_chain,
|
|
symbol,
|
|
name,
|
|
token_id,
|
|
uri,
|
|
to,
|
|
to_chain,
|
|
);
|
|
|
|
assert!(parsed_transfer == transfer, 0);
|
|
}
|
|
|
|
#[test]
|
|
public fun parse_roundtrip() {
|
|
let token_address = external_address::from_bytes(x"beef");
|
|
let token_chain = u16::from_u64(1);
|
|
let symbol = string32::from_bytes(b"HELLO");
|
|
let name = string32::from_bytes(b"hello token");
|
|
let to = external_address::from_bytes(x"cafe");
|
|
let token_id = external_address::from_bytes(x"beefcafe");
|
|
let uri = uri::from_bytes(b"http://google.com");
|
|
let to_chain = u16::from_u64(7);
|
|
let transfer = transfer::create(
|
|
token_address,
|
|
token_chain,
|
|
symbol,
|
|
name,
|
|
token_id,
|
|
uri,
|
|
to,
|
|
to_chain,
|
|
);
|
|
let parsed_transfer = transfer::parse(transfer::encode(&transfer));
|
|
assert!(parsed_transfer == transfer, 0);
|
|
}
|
|
}
|