sui_test.go 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224
  1. package txverifier
  2. import (
  3. "context"
  4. "encoding/hex"
  5. "encoding/json"
  6. "fmt"
  7. "math/big"
  8. "testing"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/wormhole-foundation/wormhole/sdk/vaa"
  11. "go.uber.org/zap"
  12. )
  13. // Tokens
  14. const (
  15. EthereumUsdcAddress = "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
  16. SuiUsdcAddress = "5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf"
  17. )
  18. func newTestSuiTransferVerifier(connection SuiApiInterface) *SuiTransferVerifier {
  19. suiCoreContract := "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a"
  20. suiTokenBridgeContract := "0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d"
  21. suiTokenBridgeEmitter := "0xccceeb29348f71bdd22ffef43a2a19c1f5b5e17c5cca5411529120182672ade5"
  22. return NewSuiTransferVerifier(suiCoreContract, suiTokenBridgeEmitter, suiTokenBridgeContract, connection)
  23. }
  24. type MockSuiApiConnection struct {
  25. // The events to be returned by QueryEvents
  26. Events []SuiEvent
  27. ObjectsResponses []SuiTryMultiGetPastObjectsResponse
  28. }
  29. type ResultTestCase struct {
  30. decimals uint8
  31. tokenChain string
  32. tokenAddress string
  33. wrapped bool
  34. newBalance string
  35. oldBalance string
  36. drop bool
  37. }
  38. func NewMockSuiApiConnection(events []SuiEvent) *MockSuiApiConnection {
  39. return &MockSuiApiConnection{
  40. Events: events,
  41. ObjectsResponses: nil,
  42. }
  43. }
  44. func (mock *MockSuiApiConnection) SetEvents(events []SuiEvent) {
  45. mock.Events = events
  46. }
  47. func (mock *MockSuiApiConnection) SetObjectsResponse(ObjectResponse SuiTryMultiGetPastObjectsResponse) {
  48. mock.ObjectsResponses = append(mock.ObjectsResponses, ObjectResponse)
  49. }
  50. func (mock *MockSuiApiConnection) ClearObjectResponses() {
  51. mock.ObjectsResponses = []SuiTryMultiGetPastObjectsResponse{}
  52. }
  53. func (mock *MockSuiApiConnection) QueryEvents(ctx context.Context, filter string, cursor string, limit int, descending bool) (SuiQueryEventsResponse, error) {
  54. return SuiQueryEventsResponse{}, nil
  55. }
  56. func (mock *MockSuiApiConnection) GetTransactionBlock(ctx context.Context, txDigest string) (SuiGetTransactionBlockResponse, error) {
  57. objectChanges := []ObjectChange{}
  58. // Create new nested object that unwraps some of it
  59. for _, objectResponse := range mock.ObjectsResponses {
  60. objectType, _ := objectResponse.GetObjectType()
  61. objectId, _ := objectResponse.GetObjectId()
  62. version, _ := objectResponse.GetVersion()
  63. previousVersion, _ := objectResponse.GetPreviousVersion()
  64. obj := ObjectChange{
  65. ObjectType: objectType,
  66. ObjectId: objectId,
  67. Version: version,
  68. PreviousVersion: previousVersion,
  69. }
  70. objectChanges = append(objectChanges, obj)
  71. }
  72. return SuiGetTransactionBlockResponse{Result: SuiGetTransactionBlockResult{Events: mock.Events, ObjectChanges: objectChanges}}, nil
  73. }
  74. func (mock *MockSuiApiConnection) TryMultiGetPastObjects(ctx context.Context, objectId string, version string, previousVersion string) (SuiTryMultiGetPastObjectsResponse, error) {
  75. for _, response := range mock.ObjectsResponses {
  76. keyIn := fmt.Sprintf("%s-%s-%s", objectId, version, previousVersion)
  77. objectId, err0 := response.GetObjectId()
  78. version, err1 := response.GetVersion()
  79. previousVersion, err2 := response.GetPreviousVersion()
  80. if err0 != nil || err1 != nil || err2 != nil {
  81. return SuiTryMultiGetPastObjectsResponse{}, fmt.Errorf("Error processing version data")
  82. }
  83. keyCur := fmt.Sprintf("%s-%s-%s", objectId, version, previousVersion)
  84. if keyIn == keyCur {
  85. return response, nil
  86. }
  87. }
  88. return SuiTryMultiGetPastObjectsResponse{}, fmt.Errorf("Can't find entry")
  89. }
  90. func TestNewSuiApiConnection(t *testing.T) {
  91. sampleUrl := "http://localhost:8080"
  92. api := NewSuiApiConnection(sampleUrl)
  93. if rpc, ok := api.(*SuiApiConnection); ok {
  94. assert.Equal(t, sampleUrl, rpc.rpc)
  95. } else {
  96. t.Errorf("Unable to get RPC from SuiApiConnection")
  97. }
  98. }
  99. func nextSequenceNumber(seq *uint64) *string {
  100. (*seq)++
  101. seqStr := fmt.Sprintf("%d", *seq)
  102. return &seqStr
  103. }
  104. func TestProcessEvents(t *testing.T) {
  105. connection := NewMockSuiApiConnection([]SuiEvent{})
  106. suiTxVerifier := newTestSuiTransferVerifier(connection)
  107. arbitraryEventType := "arbitrary::EventType"
  108. arbitraryEmitter := "0x3117"
  109. sequenceNumber := uint64(0)
  110. logger := zap.NewNop()
  111. // Constants used throughout the tests
  112. suiEventType := suiTxVerifier.suiEventType
  113. suiTokenBridgeEmitter := suiTxVerifier.suiTokenBridgeEmitter
  114. // Define test cases
  115. tests := []struct {
  116. name string
  117. events []SuiEvent
  118. expectedResult map[string]*big.Int
  119. expectedCount int
  120. }{
  121. {
  122. name: "TestNoEvents",
  123. events: []SuiEvent{},
  124. expectedResult: map[string]*big.Int{},
  125. expectedCount: 0,
  126. },
  127. {
  128. name: "TestSingleEthereumUSDCEvent",
  129. events: []SuiEvent{
  130. {
  131. Type: &suiEventType,
  132. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  133. Sender: &suiTokenBridgeEmitter,
  134. Payload: generatePayload(1, big.NewInt(100), EthereumUsdcAddress, 2),
  135. Sequence: nextSequenceNumber(&sequenceNumber),
  136. }),
  137. },
  138. },
  139. expectedResult: map[string]*big.Int{
  140. fmt.Sprintf(KEY_FORMAT, EthereumUsdcAddress, vaa.ChainIDEthereum): big.NewInt(100),
  141. },
  142. expectedCount: 1,
  143. },
  144. {
  145. name: "TestMultipleEthereumUSDCEvents",
  146. events: []SuiEvent{
  147. {
  148. Type: &suiEventType,
  149. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  150. Sender: &suiTokenBridgeEmitter,
  151. Payload: generatePayload(1, big.NewInt(100), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  152. Sequence: nextSequenceNumber(&sequenceNumber),
  153. }),
  154. },
  155. {
  156. Type: &suiEventType,
  157. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  158. Sender: &suiTokenBridgeEmitter,
  159. Payload: generatePayload(1, big.NewInt(100), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  160. Sequence: nextSequenceNumber(&sequenceNumber),
  161. }),
  162. },
  163. },
  164. expectedResult: map[string]*big.Int{
  165. fmt.Sprintf(KEY_FORMAT, EthereumUsdcAddress, vaa.ChainIDEthereum): big.NewInt(200),
  166. },
  167. expectedCount: 2,
  168. },
  169. {
  170. name: "TestMixedEthereumAndSuiUSDCEvents",
  171. events: []SuiEvent{
  172. {
  173. Type: &suiEventType,
  174. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  175. Sender: &suiTokenBridgeEmitter,
  176. Payload: generatePayload(1, big.NewInt(100), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  177. Sequence: nextSequenceNumber(&sequenceNumber),
  178. }),
  179. },
  180. {
  181. Type: &suiEventType,
  182. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  183. Sender: &suiTokenBridgeEmitter,
  184. Payload: generatePayload(1, big.NewInt(100), SuiUsdcAddress, uint16(vaa.ChainIDSui)),
  185. Sequence: nextSequenceNumber(&sequenceNumber),
  186. }),
  187. },
  188. },
  189. expectedResult: map[string]*big.Int{
  190. fmt.Sprintf(KEY_FORMAT, EthereumUsdcAddress, vaa.ChainIDEthereum): big.NewInt(100),
  191. fmt.Sprintf(KEY_FORMAT, SuiUsdcAddress, vaa.ChainIDSui): big.NewInt(100),
  192. },
  193. expectedCount: 2,
  194. },
  195. {
  196. name: "TestIncorrectSender",
  197. events: []SuiEvent{
  198. {
  199. Type: &suiEventType,
  200. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  201. Sender: &arbitraryEmitter,
  202. Payload: generatePayload(1, big.NewInt(100), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  203. Sequence: nextSequenceNumber(&sequenceNumber),
  204. }),
  205. },
  206. },
  207. expectedResult: map[string]*big.Int{},
  208. expectedCount: 0,
  209. },
  210. {
  211. name: "TestSkipNonWormholeEvents",
  212. events: []SuiEvent{
  213. {
  214. Type: &suiEventType,
  215. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  216. Sender: &suiTokenBridgeEmitter,
  217. Payload: generatePayload(1, big.NewInt(100), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  218. Sequence: nextSequenceNumber(&sequenceNumber),
  219. }),
  220. },
  221. {
  222. Type: &arbitraryEventType,
  223. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  224. Sender: &suiTokenBridgeEmitter,
  225. Payload: generatePayload(1, big.NewInt(100), SuiUsdcAddress, uint16(vaa.ChainIDSui)),
  226. Sequence: nextSequenceNumber(&sequenceNumber),
  227. }),
  228. },
  229. },
  230. expectedResult: map[string]*big.Int{
  231. fmt.Sprintf(KEY_FORMAT, EthereumUsdcAddress, vaa.ChainIDEthereum): big.NewInt(100),
  232. },
  233. expectedCount: 1,
  234. },
  235. {
  236. name: "TestInvalidWormholePayloads",
  237. events: []SuiEvent{
  238. { // Invalid payload type
  239. Type: &suiEventType,
  240. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  241. Sender: &suiTokenBridgeEmitter,
  242. Payload: generatePayload(0, big.NewInt(100), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  243. Sequence: nextSequenceNumber(&sequenceNumber),
  244. }),
  245. },
  246. { // Empty payload
  247. Type: &suiEventType,
  248. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  249. Sender: &suiTokenBridgeEmitter,
  250. Payload: []byte{},
  251. Sequence: nextSequenceNumber(&sequenceNumber),
  252. }),
  253. },
  254. },
  255. expectedResult: map[string]*big.Int{},
  256. expectedCount: 0,
  257. },
  258. }
  259. for _, tt := range tests {
  260. t.Run(tt.name, func(t *testing.T) {
  261. requests := suiTxVerifier.extractBridgeRequestsFromEvents(tt.events, logger)
  262. assert.Equal(t, tt.expectedCount, len(requests))
  263. // assert.Equal(t, tt.expectedResult, result)
  264. // assert.Equal(t, tt.expectedCount, count)
  265. })
  266. }
  267. }
  268. func TestProcessObjectUpdates(t *testing.T) {
  269. suiTxVerifier := newTestSuiTransferVerifier(nil)
  270. ctx := context.TODO()
  271. logger := zap.NewNop() // zap.Must(zap.NewDevelopment())
  272. // Constants used throughout the tests
  273. normalObjectNativeType := "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x2::sui::SUI>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::native_asset::NativeAsset<0x2::sui::SUI>>"
  274. normalObjectForeignType := "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::wrapped_asset::WrappedAsset<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>>"
  275. normalVersion := "6565"
  276. normalPreviousVersion := "4040"
  277. normalObjectNativeId := "0x831c45a8d512c9cf46e7a8a947f7cbbb5e0a59829aa72450ff26fb1873fd0e94"
  278. normalObjectForeignId := "0xf8f80c0d569fb076adb5fdc3a717dcb9ac14f7fd7512dc17efbf0f80a8b7fa8a"
  279. normalTokenAddressForeign := "0,0,0,0,0,0,0,0,0,0,0,0,160,184,105,145,198,33,139,54,193,209,157,74,46,158,176,206,54,6,235,72"
  280. normalTokenAddressNative := "146,88,24,31,92,234,200,219,255,183,3,8,144,36,60,174,214,154,149,153,210,136,109,149,122,156,183,101,106,243,189,179"
  281. normalChainIdNative := "21"
  282. normalChainIdForeign := "2"
  283. oneToken := new(big.Int)
  284. oneToken.SetString("1000000000000000000", 10)
  285. // Decimals, token chain, token address, wrapped or not, balance/custody
  286. tests := []struct {
  287. name string
  288. objectChanges []ObjectChange
  289. resultList []ResultTestCase
  290. expectedResult map[string]TransferIntoBridge
  291. expectedCount uint
  292. }{
  293. {
  294. name: "TestProcessObjectNativeBase",
  295. objectChanges: []ObjectChange{
  296. {
  297. ObjectType: normalObjectNativeType,
  298. Version: normalVersion,
  299. PreviousVersion: normalPreviousVersion,
  300. ObjectId: normalObjectNativeId,
  301. },
  302. },
  303. resultList: []ResultTestCase{
  304. {
  305. tokenChain: normalChainIdNative,
  306. tokenAddress: normalTokenAddressNative,
  307. wrapped: false,
  308. newBalance: "1000",
  309. oldBalance: "10",
  310. decimals: 8,
  311. },
  312. },
  313. expectedResult: map[string]TransferIntoBridge{
  314. "9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3-21": {Amount: big.NewInt(990)},
  315. },
  316. expectedCount: 1,
  317. },
  318. {
  319. name: "TestProcessObjectForeignBase",
  320. objectChanges: []ObjectChange{
  321. {
  322. ObjectType: normalObjectForeignType,
  323. Version: normalVersion,
  324. PreviousVersion: normalPreviousVersion,
  325. ObjectId: normalObjectForeignId,
  326. },
  327. },
  328. resultList: []ResultTestCase{
  329. {
  330. tokenChain: normalChainIdForeign,
  331. tokenAddress: normalTokenAddressForeign,
  332. wrapped: true,
  333. newBalance: "10",
  334. oldBalance: "1000",
  335. decimals: 8,
  336. },
  337. },
  338. expectedResult: map[string]TransferIntoBridge{
  339. "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48-2": {Amount: big.NewInt(990)},
  340. },
  341. expectedCount: 1,
  342. },
  343. {
  344. name: "TestProcessObjectNativeNegative",
  345. objectChanges: []ObjectChange{
  346. {
  347. ObjectType: normalObjectNativeType,
  348. Version: normalVersion,
  349. PreviousVersion: normalPreviousVersion,
  350. ObjectId: normalObjectNativeId,
  351. },
  352. },
  353. resultList: []ResultTestCase{
  354. {
  355. tokenChain: normalChainIdNative,
  356. tokenAddress: normalTokenAddressNative,
  357. wrapped: false,
  358. newBalance: "10",
  359. oldBalance: "1000",
  360. decimals: 8,
  361. },
  362. },
  363. expectedResult: map[string]TransferIntoBridge{
  364. "9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3-21": {Amount: big.NewInt(-990)},
  365. },
  366. expectedCount: 1,
  367. },
  368. {
  369. name: "TestProcessObjectForeignNegative", // Unsure if this test case is possible from Sui API
  370. objectChanges: []ObjectChange{
  371. {
  372. ObjectType: normalObjectForeignType,
  373. Version: normalVersion,
  374. PreviousVersion: normalPreviousVersion,
  375. ObjectId: normalObjectForeignId,
  376. },
  377. },
  378. resultList: []ResultTestCase{
  379. {
  380. tokenChain: normalChainIdForeign,
  381. tokenAddress: normalTokenAddressForeign,
  382. wrapped: true,
  383. newBalance: "1000",
  384. oldBalance: "10",
  385. decimals: 8,
  386. },
  387. },
  388. expectedResult: map[string]TransferIntoBridge{
  389. "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48-2": {Amount: big.NewInt(-990)},
  390. },
  391. expectedCount: 1,
  392. },
  393. {
  394. name: "TestProcessObjectNativeMultiple",
  395. objectChanges: []ObjectChange{
  396. {
  397. ObjectType: normalObjectNativeType,
  398. Version: normalVersion,
  399. PreviousVersion: normalPreviousVersion,
  400. ObjectId: normalObjectNativeId,
  401. },
  402. {
  403. ObjectType: "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0xb779486cfd6c19e9218cc7dc17c453014d2d9ba12d2ee4dbb0ec4e1e02ae1cca::spt::SPT>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::native_asset::NativeAsset<0xb779486cfd6c19e9218cc7dc17c453014d2d9ba12d2ee4dbb0ec4e1e02ae1cca::spt::SPT>>",
  404. Version: normalVersion,
  405. PreviousVersion: normalPreviousVersion,
  406. ObjectId: "0x0063d37cdce648a7c6f72f69a75a114fbcc81ef23300e4ace60c7941521163db",
  407. },
  408. },
  409. resultList: []ResultTestCase{
  410. {
  411. tokenChain: normalChainIdNative,
  412. tokenAddress: normalTokenAddressNative,
  413. wrapped: false,
  414. newBalance: "1000",
  415. oldBalance: "10",
  416. decimals: 8,
  417. },
  418. {
  419. tokenChain: normalChainIdNative,
  420. tokenAddress: "80,117,89,76,1,212,111,59,203,196,167,239,20,98,5,130,115,190,206,119,147,238,189,4,100,150,53,151,201,253,9,53",
  421. wrapped: false,
  422. newBalance: "5000",
  423. oldBalance: "50",
  424. decimals: 8,
  425. },
  426. },
  427. expectedResult: map[string]TransferIntoBridge{
  428. "9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3-21": {Amount: big.NewInt(990)},
  429. "5075594c01d46f3bcbc4a7ef1462058273bece7793eebd0464963597c9fd0935-21": {Amount: big.NewInt(4950)},
  430. },
  431. expectedCount: 2,
  432. },
  433. {
  434. name: "TestProcessObjectNativeAndForeign",
  435. objectChanges: []ObjectChange{
  436. {
  437. ObjectType: normalObjectNativeType,
  438. Version: normalVersion,
  439. PreviousVersion: normalPreviousVersion,
  440. ObjectId: normalObjectNativeId,
  441. },
  442. {
  443. ObjectType: normalObjectForeignType,
  444. Version: normalVersion,
  445. PreviousVersion: normalPreviousVersion,
  446. ObjectId: normalObjectForeignId,
  447. },
  448. },
  449. resultList: []ResultTestCase{
  450. {
  451. tokenChain: normalChainIdNative,
  452. tokenAddress: normalTokenAddressNative,
  453. wrapped: false,
  454. newBalance: "1000",
  455. oldBalance: "10",
  456. decimals: 8,
  457. },
  458. {
  459. tokenChain: normalChainIdForeign,
  460. tokenAddress: normalTokenAddressForeign,
  461. wrapped: true,
  462. newBalance: "50",
  463. oldBalance: "5000",
  464. decimals: 8,
  465. },
  466. },
  467. expectedResult: map[string]TransferIntoBridge{
  468. "9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3-21": {Amount: big.NewInt(990)},
  469. "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48-2": {Amount: big.NewInt(4950)},
  470. },
  471. expectedCount: 2,
  472. },
  473. {
  474. name: "TestProcessObjectWrongPackageIdType",
  475. objectChanges: []ObjectChange{
  476. {
  477. ObjectType: "0x2::dynamic_field::Field<0xa340e3db1332c21f20f5c08bef0fa459e733575f9a7e2f5faca64f72cd5a54f2::token_registry::Key<0x2::sui::SUI>, 0xa340e3db1332c21f20f5c08bef0fa459e733575f9a7e2f5faca64f72cd5a54f2::native_asset::NativeAsset<0x2::sui::SUI>",
  478. Version: normalVersion,
  479. PreviousVersion: normalPreviousVersion,
  480. ObjectId: normalObjectNativeId,
  481. },
  482. },
  483. resultList: []ResultTestCase{
  484. {
  485. tokenChain: normalChainIdNative,
  486. tokenAddress: normalTokenAddressNative,
  487. wrapped: false,
  488. newBalance: "1000",
  489. oldBalance: "10",
  490. decimals: 8,
  491. },
  492. },
  493. expectedResult: map[string]TransferIntoBridge{},
  494. expectedCount: 0,
  495. },
  496. {
  497. name: "TestProcessObjectNotDynamicField",
  498. objectChanges: []ObjectChange{
  499. {
  500. ObjectType: "0x11111111111111111111::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x2::sui::SUI>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::native_asset::NativeAsset<0x2::sui::SUI>",
  501. Version: normalVersion,
  502. PreviousVersion: normalPreviousVersion,
  503. ObjectId: normalObjectNativeId,
  504. },
  505. },
  506. resultList: []ResultTestCase{
  507. {
  508. tokenChain: normalChainIdNative,
  509. tokenAddress: normalTokenAddressNative,
  510. wrapped: false,
  511. newBalance: "1000",
  512. oldBalance: "10",
  513. decimals: 8,
  514. },
  515. },
  516. expectedResult: map[string]TransferIntoBridge{},
  517. expectedCount: 0,
  518. },
  519. {
  520. name: "TestProcessObjectMismatchedCoinTypes",
  521. objectChanges: []ObjectChange{
  522. {
  523. ObjectType: "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x2::sui::SUI>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::native_asset::NativeAsset<0x11111111111111111111::sui::SUI>",
  524. Version: normalVersion,
  525. PreviousVersion: normalPreviousVersion,
  526. ObjectId: normalObjectNativeId,
  527. },
  528. },
  529. resultList: []ResultTestCase{
  530. {
  531. tokenChain: normalChainIdNative,
  532. tokenAddress: normalTokenAddressNative,
  533. wrapped: false,
  534. newBalance: "1000",
  535. oldBalance: "10",
  536. decimals: 8,
  537. },
  538. },
  539. expectedResult: map[string]TransferIntoBridge{},
  540. expectedCount: 0,
  541. },
  542. {
  543. name: "TestProcessObjectNotAssetType",
  544. objectChanges: []ObjectChange{
  545. {
  546. ObjectType: "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x2::sui::SUI>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::not_native_asset::NativeAsset<0x2::sui::SUI>",
  547. Version: normalVersion,
  548. PreviousVersion: normalPreviousVersion,
  549. ObjectId: normalObjectNativeId,
  550. },
  551. },
  552. resultList: []ResultTestCase{
  553. {
  554. tokenChain: normalChainIdNative,
  555. tokenAddress: normalTokenAddressNative,
  556. wrapped: false,
  557. newBalance: "1000",
  558. oldBalance: "10",
  559. decimals: 8,
  560. },
  561. },
  562. expectedResult: map[string]TransferIntoBridge{},
  563. expectedCount: 0,
  564. },
  565. {
  566. name: "TestProcessObjectOneGoodOneBad",
  567. objectChanges: []ObjectChange{
  568. {
  569. ObjectType: normalObjectForeignType,
  570. Version: normalVersion,
  571. PreviousVersion: normalPreviousVersion,
  572. ObjectId: normalObjectForeignId,
  573. },
  574. {
  575. ObjectType: "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x2::sui::SUI>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::not_native_asset::NativeAsset<0x2::sui::SUI>",
  576. Version: fmt.Sprintf("%s111", normalVersion),
  577. PreviousVersion: normalPreviousVersion,
  578. ObjectId: normalObjectNativeId,
  579. },
  580. },
  581. resultList: []ResultTestCase{
  582. {
  583. tokenChain: normalChainIdForeign,
  584. tokenAddress: normalTokenAddressForeign,
  585. wrapped: true,
  586. newBalance: "10",
  587. oldBalance: "1000",
  588. decimals: 8,
  589. },
  590. {
  591. tokenChain: normalChainIdNative,
  592. tokenAddress: normalTokenAddressNative,
  593. wrapped: false,
  594. newBalance: "1000",
  595. oldBalance: "10",
  596. decimals: 8,
  597. },
  598. },
  599. expectedResult: map[string]TransferIntoBridge{
  600. "000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48-2": {Amount: big.NewInt(990)},
  601. },
  602. expectedCount: 1,
  603. },
  604. {
  605. name: "TestProcessObjectRealNumbers",
  606. objectChanges: []ObjectChange{
  607. {
  608. ObjectType: normalObjectNativeType,
  609. Version: normalVersion,
  610. PreviousVersion: normalPreviousVersion,
  611. ObjectId: normalObjectNativeId,
  612. },
  613. },
  614. resultList: []ResultTestCase{
  615. {
  616. tokenChain: normalChainIdNative,
  617. tokenAddress: normalTokenAddressNative,
  618. wrapped: false,
  619. newBalance: "1000000000000000",
  620. oldBalance: "999999000000000",
  621. decimals: 8,
  622. },
  623. },
  624. expectedResult: map[string]TransferIntoBridge{
  625. "9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3-21": {Amount: big.NewInt(1000000000)},
  626. },
  627. expectedCount: 1,
  628. },
  629. {
  630. name: "TestProcessObjectNormalize",
  631. objectChanges: []ObjectChange{
  632. {
  633. ObjectType: normalObjectNativeType,
  634. Version: normalVersion,
  635. PreviousVersion: normalPreviousVersion,
  636. ObjectId: normalObjectNativeId,
  637. },
  638. },
  639. resultList: []ResultTestCase{
  640. {
  641. tokenChain: normalChainIdNative,
  642. tokenAddress: normalTokenAddressNative,
  643. wrapped: false,
  644. newBalance: "101000000000000000000",
  645. oldBalance: "100000000000000000000",
  646. decimals: 18,
  647. },
  648. },
  649. expectedResult: map[string]TransferIntoBridge{
  650. "9258181f5ceac8dbffb7030890243caed69a9599d2886d957a9cb7656af3bdb3-21": {Amount: big.NewInt(100000000)},
  651. },
  652. expectedCount: 1,
  653. },
  654. {
  655. name: "TestProcessObjectMissingVersion",
  656. objectChanges: []ObjectChange{
  657. {
  658. ObjectType: normalObjectNativeType,
  659. Version: normalVersion,
  660. PreviousVersion: normalPreviousVersion,
  661. ObjectId: normalObjectNativeId,
  662. },
  663. },
  664. resultList: []ResultTestCase{
  665. {
  666. tokenChain: normalChainIdNative,
  667. tokenAddress: normalTokenAddressNative,
  668. wrapped: false,
  669. newBalance: "1000",
  670. oldBalance: "10",
  671. decimals: 8,
  672. drop: true,
  673. },
  674. },
  675. expectedResult: map[string]TransferIntoBridge{},
  676. expectedCount: 0,
  677. },
  678. }
  679. for _, tt := range tests {
  680. t.Run(tt.name, func(t *testing.T) {
  681. connection := NewMockSuiApiConnection([]SuiEvent{})
  682. suiTxVerifier.suiApiConnection = connection
  683. assert.Equal(t, len(tt.objectChanges), len(tt.resultList))
  684. // Add all changes to the mock Sui API for future lookups
  685. for index := 0; index < len(tt.objectChanges); index++ {
  686. change := tt.objectChanges[index]
  687. queryResult := tt.resultList[index]
  688. if !queryResult.drop {
  689. responseObject := generateResponsesObject(change.ObjectId, change.Version, change.ObjectType, change.PreviousVersion, queryResult.newBalance, queryResult.oldBalance, queryResult.tokenAddress, queryResult.tokenChain, queryResult.decimals, queryResult.wrapped)
  690. connection.SetObjectsResponse(responseObject)
  691. }
  692. }
  693. // Run function and check results
  694. transfers := suiTxVerifier.extractTransfersIntoBridgeFromObjectChanges(ctx, tt.objectChanges, logger)
  695. // Check that expectedResult and transfers have same number of keys
  696. assert.Equal(t, uint(len(tt.expectedResult)), uint(len(transfers)))
  697. // Check that each key in expectedResult exists in transfers and has the expected amount
  698. for key, expectedValue := range tt.expectedResult {
  699. actualValue, exists := transfers[key]
  700. if !exists {
  701. t.Errorf("Expected key %s not found in result", key)
  702. } else if actualValue.Amount.Cmp(expectedValue.Amount) != 0 {
  703. t.Errorf("For key %s, expected amount %s but got %s", key, expectedValue.Amount.String(), actualValue.Amount.String())
  704. }
  705. }
  706. })
  707. }
  708. }
  709. func TestProcessDigest(t *testing.T) {
  710. suiTxVerifier := newTestSuiTransferVerifier(nil)
  711. // Constants used throughout the tests
  712. normalObjectNativeType := "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x2::sui::SUI>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::native_asset::NativeAsset<0x2::sui::SUI>>"
  713. normalObjectForeignType := "0x2::dynamic_field::Field<0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>, 0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::wrapped_asset::WrappedAsset<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>>"
  714. normalVersion := "6565"
  715. normalPreviousVersion := "4040"
  716. normalObjectNativeId := "0x831c45a8d512c9cf46e7a8a947f7cbbb5e0a59829aa72450ff26fb1873fd0e94"
  717. normalObjectForeignId := "0xf8f80c0d569fb076adb5fdc3a717dcb9ac14f7fd7512dc17efbf0f80a8b7fa8a"
  718. normalTokenAddressForeign := "0,0,0,0,0,0,0,0,0,0,0,0,160,184,105,145,198,33,139,54,193,209,157,74,46,158,176,206,54,6,235,72"
  719. normalTokenAddressNative := "93,75,48,37,6,100,92,55,255,19,59,152,196,181,10,90,225,72,65,101,151,56,214,215,51,213,157,13,33,122,147,191"
  720. normalChainIdNative := "21"
  721. normalChainIdForeign := "2"
  722. suiEventType := suiTxVerifier.suiEventType
  723. suiTokenBridgeEmitter := suiTxVerifier.suiTokenBridgeEmitter
  724. sequenceNumber := uint64(0)
  725. logger := zap.Must(zap.NewDevelopment())
  726. // func processDigest(digest string, suiApiConnection SuiApiInterface, logger *zap.Logger) error {
  727. // Needs BOTH events and ObjectChange information to be updated
  728. tests := []struct {
  729. name string
  730. objectChanges []ObjectChange
  731. resultList []ResultTestCase
  732. suiEvents []SuiEvent
  733. expectedError error
  734. expectedCount uint
  735. }{
  736. {
  737. name: "TestProcessDigestNativeBase",
  738. objectChanges: []ObjectChange{
  739. {
  740. ObjectType: normalObjectNativeType,
  741. Version: normalVersion,
  742. PreviousVersion: normalPreviousVersion,
  743. ObjectId: normalObjectNativeId,
  744. },
  745. },
  746. resultList: []ResultTestCase{
  747. {
  748. tokenChain: normalChainIdNative,
  749. tokenAddress: normalTokenAddressNative,
  750. wrapped: false,
  751. newBalance: "1000",
  752. oldBalance: "10",
  753. decimals: 8,
  754. },
  755. },
  756. suiEvents: []SuiEvent{
  757. {
  758. Type: &suiEventType,
  759. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  760. Sender: &suiTokenBridgeEmitter,
  761. Payload: generatePayload(1, big.NewInt(990), SuiUsdcAddress, uint16(vaa.ChainIDSui)),
  762. }),
  763. },
  764. },
  765. expectedError: nil,
  766. expectedCount: 1,
  767. },
  768. {
  769. name: "TestProcessDigestTakingMoreThanPuttingIn",
  770. objectChanges: []ObjectChange{
  771. {
  772. ObjectType: normalObjectNativeType,
  773. Version: normalVersion,
  774. PreviousVersion: normalPreviousVersion,
  775. ObjectId: normalObjectNativeId,
  776. },
  777. },
  778. resultList: []ResultTestCase{
  779. {
  780. tokenChain: normalChainIdNative,
  781. tokenAddress: normalTokenAddressNative,
  782. wrapped: false,
  783. newBalance: "100000",
  784. oldBalance: "100000",
  785. decimals: 8,
  786. },
  787. },
  788. suiEvents: []SuiEvent{
  789. {
  790. Type: &suiEventType,
  791. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  792. Sender: &suiTokenBridgeEmitter,
  793. Payload: generatePayload(1, big.NewInt(100000), SuiUsdcAddress, uint16(vaa.ChainIDSui)),
  794. Sequence: nextSequenceNumber(&sequenceNumber),
  795. }),
  796. },
  797. },
  798. expectedError: &InvariantError{Msg: INVARIANT_INSUFFICIENT_DEPOSIT},
  799. expectedCount: 0,
  800. },
  801. {
  802. name: "TestProcessDigestNoEvents",
  803. objectChanges: []ObjectChange{},
  804. resultList: []ResultTestCase{},
  805. suiEvents: []SuiEvent{
  806. {
  807. Type: &suiEventType,
  808. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  809. Sender: &suiTokenBridgeEmitter,
  810. Payload: generatePayload(1, big.NewInt(100000), SuiUsdcAddress, uint16(vaa.ChainIDSui)),
  811. Sequence: nextSequenceNumber(&sequenceNumber),
  812. }),
  813. },
  814. },
  815. expectedError: &InvariantError{Msg: INVARIANT_NO_DEPOSIT},
  816. expectedCount: 0,
  817. },
  818. {
  819. name: "TestProcessDigestForeignBase",
  820. objectChanges: []ObjectChange{
  821. {
  822. ObjectType: normalObjectForeignType,
  823. Version: normalVersion,
  824. PreviousVersion: normalPreviousVersion,
  825. ObjectId: normalObjectForeignId,
  826. },
  827. },
  828. resultList: []ResultTestCase{
  829. {
  830. tokenChain: normalChainIdForeign,
  831. tokenAddress: normalTokenAddressForeign,
  832. wrapped: true,
  833. newBalance: "10",
  834. oldBalance: "1000",
  835. decimals: 8,
  836. },
  837. },
  838. suiEvents: []SuiEvent{
  839. {
  840. Type: &suiEventType,
  841. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  842. Sender: &suiTokenBridgeEmitter,
  843. Payload: generatePayload(1, big.NewInt(990), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  844. Sequence: nextSequenceNumber(&sequenceNumber),
  845. }),
  846. },
  847. },
  848. expectedError: nil,
  849. expectedCount: 1,
  850. },
  851. {
  852. name: "TestProcessDigestNoEvents",
  853. objectChanges: []ObjectChange{},
  854. resultList: []ResultTestCase{},
  855. suiEvents: []SuiEvent{
  856. {
  857. Type: &suiEventType,
  858. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  859. Sender: &suiTokenBridgeEmitter,
  860. Payload: generatePayload(1, big.NewInt(100000), SuiUsdcAddress, uint16(vaa.ChainIDSui)),
  861. Sequence: nextSequenceNumber(&sequenceNumber),
  862. }),
  863. },
  864. },
  865. expectedError: &InvariantError{Msg: INVARIANT_NO_DEPOSIT},
  866. expectedCount: 0,
  867. },
  868. {
  869. name: "TestProcessDigestMultipleEvents",
  870. objectChanges: []ObjectChange{
  871. {
  872. ObjectType: normalObjectForeignType,
  873. Version: normalVersion,
  874. PreviousVersion: normalPreviousVersion,
  875. ObjectId: normalObjectForeignId,
  876. },
  877. },
  878. resultList: []ResultTestCase{
  879. {
  880. tokenChain: normalChainIdForeign,
  881. tokenAddress: normalTokenAddressForeign,
  882. wrapped: true,
  883. newBalance: "10",
  884. oldBalance: "2000",
  885. decimals: 8,
  886. },
  887. },
  888. suiEvents: []SuiEvent{
  889. {
  890. Type: &suiEventType,
  891. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  892. Sender: &suiTokenBridgeEmitter,
  893. Payload: generatePayload(1, big.NewInt(990), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  894. Sequence: nextSequenceNumber(&sequenceNumber),
  895. }),
  896. },
  897. {
  898. Type: &suiEventType,
  899. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  900. Sender: &suiTokenBridgeEmitter,
  901. Payload: generatePayload(1, big.NewInt(1000), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  902. }),
  903. },
  904. },
  905. expectedError: nil,
  906. expectedCount: 2,
  907. },
  908. {
  909. name: "TestProcessDigestMultipleEventsOverWithdraw",
  910. objectChanges: []ObjectChange{
  911. {
  912. ObjectType: normalObjectForeignType,
  913. Version: normalVersion,
  914. PreviousVersion: normalPreviousVersion,
  915. ObjectId: normalObjectForeignId,
  916. },
  917. },
  918. resultList: []ResultTestCase{
  919. {
  920. tokenChain: normalChainIdForeign,
  921. tokenAddress: normalTokenAddressForeign,
  922. wrapped: true,
  923. newBalance: "10",
  924. oldBalance: "2000",
  925. decimals: 8,
  926. },
  927. },
  928. suiEvents: []SuiEvent{
  929. {
  930. Type: &suiEventType,
  931. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  932. Sender: &suiTokenBridgeEmitter,
  933. Payload: generatePayload(1, big.NewInt(990), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  934. Sequence: nextSequenceNumber(&sequenceNumber),
  935. }),
  936. },
  937. {
  938. Type: &suiEventType,
  939. ParsedJson: uncheckedJsonMarshal(&WormholeMessage{
  940. Sender: &suiTokenBridgeEmitter,
  941. Payload: generatePayload(1, big.NewInt(1001), EthereumUsdcAddress, uint16(vaa.ChainIDEthereum)),
  942. Sequence: nextSequenceNumber(&sequenceNumber),
  943. }),
  944. },
  945. },
  946. expectedError: &InvariantError{Msg: INVARIANT_INSUFFICIENT_DEPOSIT},
  947. expectedCount: 0,
  948. },
  949. }
  950. for _, tt := range tests {
  951. t.Run(tt.name, func(t *testing.T) {
  952. ctx := context.TODO()
  953. assert.Equal(t, len(tt.objectChanges), len(tt.resultList))
  954. connection := NewMockSuiApiConnection(tt.suiEvents) // Set events for connection
  955. suiTxVerifier.suiApiConnection = connection
  956. // Add Object Response data for Sui connections
  957. for index := 0; index < len(tt.objectChanges); index++ {
  958. change := tt.objectChanges[index]
  959. queryResult := tt.resultList[index]
  960. responseObject := generateResponsesObject(change.ObjectId, change.Version, change.ObjectType, change.PreviousVersion, queryResult.newBalance, queryResult.oldBalance, queryResult.tokenAddress, queryResult.tokenChain, queryResult.decimals, queryResult.wrapped)
  961. connection.SetObjectsResponse(responseObject)
  962. }
  963. _, err := suiTxVerifier.processDigestInternal(ctx, "HASH", "", logger)
  964. assert.Equal(t, true, tt.expectedError == nil && err == nil || err != nil && err.Error() == tt.expectedError.Error())
  965. // assert.Equal(t, tt.expectedCount, numProcessed)
  966. })
  967. }
  968. }
  969. // Marshal the input to a json.RawMessage, and ignore the error message.
  970. func uncheckedJsonMarshal(v any) *json.RawMessage {
  971. data, _ := json.Marshal(v)
  972. return (*json.RawMessage)(&data)
  973. }
  974. // Generate WormholeMessage payload.
  975. //
  976. // Payload type: payload[0]
  977. // Amount: payload[1] for 32
  978. // Origin address: payload[33] for 32
  979. // Origin chain ID: payload[65] for 2
  980. func generatePayload(payloadType byte, amount *big.Int, originAddressHex string, originChainID uint16) []byte {
  981. originAddress, _ := hex.DecodeString(originAddressHex)
  982. payload := make([]byte, 0, 101)
  983. // Append payload type
  984. payload = append(payload, payloadType)
  985. // Append amount (32 bytes)
  986. amountBytes := amount.FillBytes(make([]byte, 32))
  987. payload = append(payload, amountBytes...)
  988. // Append origin address (32 bytes)
  989. payload = append(payload, originAddress...)
  990. // Append origin chain ID (2 bytes)
  991. originChainIDBytes := []byte{byte(originChainID >> 8), byte(originChainID & 0xff)}
  992. payload = append(payload, originChainIDBytes...)
  993. // Right-pad the payload to 101 bytes
  994. padding := make([]byte, 101-len(payload))
  995. payload = append(payload, padding...)
  996. return payload
  997. }
  998. /*
  999. JSON data
  1000. Decimals, token chain, token address, wrapped or not, balance/custody
  1001. */
  1002. func generateResponsesObject(objectId string, version string, objectType string, previousVersion string, balanceAfter string, balanceBefore string, tokenAddress string, tokenChain string, decimals uint8, isWrapped bool) SuiTryMultiGetPastObjectsResponse {
  1003. var newVersion string
  1004. var oldVersion string
  1005. if isWrapped == false {
  1006. newVersion = generateResponseObjectNative(objectId, version, objectType, balanceAfter, tokenAddress, decimals)
  1007. oldVersion = generateResponseObjectNative(objectId, previousVersion, objectType, balanceBefore, tokenAddress, decimals)
  1008. } else {
  1009. newVersion = generateResponseObjectForeign(objectId, version, objectType, balanceAfter, tokenAddress, tokenChain, decimals)
  1010. oldVersion = generateResponseObjectForeign(objectId, previousVersion, objectType, balanceBefore, tokenAddress, tokenChain, decimals)
  1011. }
  1012. // Complete the rest of the response data
  1013. responseString := fmt.Sprintf(`{"result": [{"details" : %s}, {"details" : %s}]}`, newVersion, oldVersion)
  1014. data := SuiTryMultiGetPastObjectsResponse{}
  1015. err := json.Unmarshal([]byte(responseString), &data)
  1016. if err != nil {
  1017. fmt.Println("Error in JSON parsing...")
  1018. }
  1019. return data
  1020. }
  1021. func generateResponseObjectNative(objectId string, version string, objectType string, balance string, tokenAddress string, decimals uint8) string {
  1022. json_string_per_object := fmt.Sprintf(`{
  1023. "objectId": "%s",
  1024. "version": "%s",
  1025. "digest": "4ne8fjG16hAXP8GxuXzoA5hBwuHz6C4D7cyf4TZza4Pa",
  1026. "type": "%s",
  1027. "owner": {
  1028. "ObjectOwner": "0x334881831bd89287554a6121087e498fa023ce52c037001b53a4563a00a281a5"
  1029. },
  1030. "previousTransaction": "FRx1iHA3Wq2ybDe3hhMSkS5yqsKJ4wUDUWY3Xp8K6g18",
  1031. "storageRebate": "3146400",
  1032. "content": {
  1033. "type": "%s",
  1034. "fields": {
  1035. "id": {
  1036. "id": "0x831c45a8d512c9cf46e7a8a947f7cbbb5e0a59829aa72450ff26fb1873fd0e94"
  1037. },
  1038. "name": {
  1039. "type": "0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x2::sui::SUI>",
  1040. "fields": {
  1041. "dummy_field": false
  1042. }
  1043. },
  1044. "value": {
  1045. "type": "0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::native_asset::NativeAsset<0x2::sui::SUI>",
  1046. "fields": {
  1047. "custody": "%s",
  1048. "decimals": %d,
  1049. "token_address": {
  1050. "type": "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a::external_address::ExternalAddress",
  1051. "fields": {
  1052. "value": {
  1053. "type": "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a::bytes32::Bytes32",
  1054. "fields": {
  1055. "data": [
  1056. %s
  1057. ]
  1058. }
  1059. }
  1060. }
  1061. }
  1062. }
  1063. }
  1064. }}}`, objectId, version, objectType, objectType, balance, decimals, tokenAddress)
  1065. return json_string_per_object
  1066. }
  1067. func generateResponseObjectForeign(objectId string, version string, objectType string, balance string, tokenAddress string, tokenChain string, decimals uint8) string {
  1068. json_string_per_object := fmt.Sprintf(`{
  1069. "objectId": "%s",
  1070. "version": "%s",
  1071. "digest": "CWXv7KJrNawMqREtVYCRT9PVF2H8cogW1WCLMd5iQchr",
  1072. "type": "%s",
  1073. "owner": {
  1074. "ObjectOwner": "0x334881831bd89287554a6121087e498fa023ce52c037001b53a4563a00a281a5"
  1075. },
  1076. "previousTransaction": "EaqLzHQTeiPq2FjYCRobDH5E91DAVZgKgZzwQUJ5FaNU",
  1077. "storageRebate": "4050800",
  1078. "content": {
  1079. "dataType": "moveObject",
  1080. "type": "%s",
  1081. "hasPublicTransfer": false,
  1082. "fields": {
  1083. "id": {
  1084. "id": "0xf8f80c0d569fb076adb5fdc3a717dcb9ac14f7fd7512dc17efbf0f80a8b7fa8a"
  1085. },
  1086. "name": {
  1087. "type": "0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::token_registry::Key<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
  1088. "fields": {
  1089. "dummy_field": false
  1090. }
  1091. },
  1092. "value": {
  1093. "type": "0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::wrapped_asset::WrappedAsset<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
  1094. "fields": {
  1095. "decimals": 6,
  1096. "info": {
  1097. "type": "0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d::wrapped_asset::ForeignInfo<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
  1098. "fields": {
  1099. "native_decimals": %d,
  1100. "symbol": "USDC",
  1101. "token_address": {
  1102. "type": "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a::external_address::ExternalAddress",
  1103. "fields": {
  1104. "value": {
  1105. "type": "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a::bytes32::Bytes32",
  1106. "fields": {"data": [%s]
  1107. }
  1108. }
  1109. }
  1110. },
  1111. "token_chain": %s
  1112. }
  1113. },
  1114. "treasury_cap": {
  1115. "type": "0x2::coin::TreasuryCap<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
  1116. "fields": {
  1117. "id": {
  1118. "id": "0xa5085139fdeae133cf6ca58f1f1cee138f24ad6fc54d8e24a519dc24f3b2b974"
  1119. },
  1120. "total_supply": {
  1121. "type": "0x2::balance::Supply<0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf::coin::COIN>",
  1122. "fields": {
  1123. "value": "%s"
  1124. }
  1125. }
  1126. }
  1127. },
  1128. "upgrade_cap": {
  1129. "type": "0x2::package::UpgradeCap",
  1130. "fields": {
  1131. "id": {
  1132. "id": "0x86ebd31cc715928671ac05e29e85b68ae1d96db02565b5413084fcb5afb695b1"
  1133. },
  1134. "package": "0x5d4b302506645c37ff133b98c4b50a5ae14841659738d6d733d59d0d217a93bf",
  1135. "policy": 0,
  1136. "version": "1"
  1137. }
  1138. }
  1139. }
  1140. }
  1141. }
  1142. }
  1143. }`, objectId, version, objectType, objectType, decimals, tokenAddress, tokenChain, balance)
  1144. return json_string_per_object
  1145. }