diff --git a/CHANGELOG.md b/CHANGELOG.md index ecfde22..52f40db 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,330 +1,282 @@ ## [1.6.0-alpha.1](https://github.com/TrustVC/trustvc/compare/v1.5.3...v1.6.0-alpha.1) (2025-06-30) - ### Features -* token registry functions ([#74](https://github.com/TrustVC/trustvc/issues/74)) ([5690fcd](https://github.com/TrustVC/trustvc/commit/5690fcda798192609b0060fab0a3f3f77dca8012)) - +- token registry functions ([#74](https://github.com/TrustVC/trustvc/issues/74)) ([5690fcd](https://github.com/TrustVC/trustvc/commit/5690fcda798192609b0060fab0a3f3f77dca8012)) ### Miscellaneous Chores -* back merge ([#75](https://github.com/TrustVC/trustvc/issues/75)) ([7cc1891](https://github.com/TrustVC/trustvc/commit/7cc1891ffebceb4eebf1421d3bc348926efa3f10)), closes [#72](https://github.com/TrustVC/trustvc/issues/72) [#72](https://github.com/TrustVC/trustvc/issues/72) [#73](https://github.com/TrustVC/trustvc/issues/73) [#73](https://github.com/TrustVC/trustvc/issues/73) +- back merge ([#75](https://github.com/TrustVC/trustvc/issues/75)) ([7cc1891](https://github.com/TrustVC/trustvc/commit/7cc1891ffebceb4eebf1421d3bc348926efa3f10)), closes [#72](https://github.com/TrustVC/trustvc/issues/72) [#72](https://github.com/TrustVC/trustvc/issues/72) [#73](https://github.com/TrustVC/trustvc/issues/73) [#73](https://github.com/TrustVC/trustvc/issues/73) ## [1.5.5](https://github.com/TrustVC/trustvc/compare/v1.5.4...v1.5.5) (2025-06-18) - ### Bug Fixes -* upgrade package ([#73](https://github.com/TrustVC/trustvc/issues/73)) ([3c6c9c7](https://github.com/TrustVC/trustvc/commit/3c6c9c73675ec43e0514d9afe5df4444f6e4400b)) +- upgrade package ([#73](https://github.com/TrustVC/trustvc/issues/73)) ([3c6c9c7](https://github.com/TrustVC/trustvc/commit/3c6c9c73675ec43e0514d9afe5df4444f6e4400b)) ## [1.5.4](https://github.com/TrustVC/trustvc/compare/v1.5.3...v1.5.4) (2025-06-17) - ### Bug Fixes -* add w3c credential status check ([#72](https://github.com/TrustVC/trustvc/issues/72)) ([0111cb3](https://github.com/TrustVC/trustvc/commit/0111cb3e48ac86f0cea715ceb5277d199f139763)) +- add w3c credential status check ([#72](https://github.com/TrustVC/trustvc/issues/72)) ([0111cb3](https://github.com/TrustVC/trustvc/commit/0111cb3e48ac86f0cea715ceb5277d199f139763)) ## [1.5.3](https://github.com/TrustVC/trustvc/compare/v1.5.2...v1.5.3) (2025-06-13) - ### Bug Fixes -* redacted document check ([#71](https://github.com/TrustVC/trustvc/issues/71)) ([413e295](https://github.com/TrustVC/trustvc/commit/413e295e5b53cace1bd86f8ec88ece0df265fce6)) +- redacted document check ([#71](https://github.com/TrustVC/trustvc/issues/71)) ([413e295](https://github.com/TrustVC/trustvc/commit/413e295e5b53cace1bd86f8ec88ece0df265fce6)) ## [1.5.2](https://github.com/TrustVC/trustvc/compare/v1.5.1...v1.5.2) (2025-06-10) - ### Bug Fixes -* useEndorsementChain fetch log error ([#70](https://github.com/TrustVC/trustvc/issues/70)) ([3d9871a](https://github.com/TrustVC/trustvc/commit/3d9871a601b05beab88f79814c9b1b4fca4b4145)) +- useEndorsementChain fetch log error ([#70](https://github.com/TrustVC/trustvc/issues/70)) ([3d9871a](https://github.com/TrustVC/trustvc/commit/3d9871a601b05beab88f79814c9b1b4fca4b4145)) ## [1.5.1](https://github.com/TrustVC/trustvc/compare/v1.5.0...v1.5.1) (2025-06-09) - ### Bug Fixes -* expose contracts ([#69](https://github.com/TrustVC/trustvc/issues/69)) ([c4e191d](https://github.com/TrustVC/trustvc/commit/c4e191de42d2a53430cd8d44896b3b6ad7a8a0c5)) +- expose contracts ([#69](https://github.com/TrustVC/trustvc/issues/69)) ([c4e191d](https://github.com/TrustVC/trustvc/commit/c4e191de42d2a53430cd8d44896b3b6ad7a8a0c5)) ## [1.5.0](https://github.com/TrustVC/trustvc/compare/v1.4.12...v1.5.0) (2025-06-09) - ### Features -* add aws kms signer ([#68](https://github.com/TrustVC/trustvc/issues/68)) ([975f82e](https://github.com/TrustVC/trustvc/commit/975f82e19ea1376f9a3b1bd77ca44cf843adbecd)) +- add aws kms signer ([#68](https://github.com/TrustVC/trustvc/issues/68)) ([975f82e](https://github.com/TrustVC/trustvc/commit/975f82e19ea1376f9a3b1bd77ca44cf843adbecd)) ## [1.4.12](https://github.com/TrustVC/trustvc/compare/v1.4.11...v1.4.12) (2025-05-30) - ### Bug Fixes -* resolve import and add provider ([#65](https://github.com/TrustVC/trustvc/issues/65)) ([6b3dae3](https://github.com/TrustVC/trustvc/commit/6b3dae3a630edb5a259b04a3bc5f39ac3a9a4fa4)) +- resolve import and add provider ([#65](https://github.com/TrustVC/trustvc/issues/65)) ([6b3dae3](https://github.com/TrustVC/trustvc/commit/6b3dae3a630edb5a259b04a3bc5f39ac3a9a4fa4)) ## [1.4.11](https://github.com/TrustVC/trustvc/compare/v1.4.10...v1.4.11) (2025-05-29) - ### Bug Fixes -* update tt utils version ([#64](https://github.com/TrustVC/trustvc/issues/64)) ([86ff7fc](https://github.com/TrustVC/trustvc/commit/86ff7fc9ac7b7d43e3e5851a0eecd94e1a5e4e15)) +- update tt utils version ([#64](https://github.com/TrustVC/trustvc/issues/64)) ([86ff7fc](https://github.com/TrustVC/trustvc/commit/86ff7fc9ac7b7d43e3e5851a0eecd94e1a5e4e15)) ## [1.4.10](https://github.com/TrustVC/trustvc/compare/v1.4.9...v1.4.10) (2025-05-21) - ### Bug Fixes -* update amoy rpc for test case ([#63](https://github.com/TrustVC/trustvc/issues/63)) ([9ddba30](https://github.com/TrustVC/trustvc/commit/9ddba300f4f240d9ec2508528bee791e481d8c04)) -* update package ([#62](https://github.com/TrustVC/trustvc/issues/62)) ([f4f3942](https://github.com/TrustVC/trustvc/commit/f4f3942e84325e69c37e652002fa41e970a09ad0)) +- update amoy rpc for test case ([#63](https://github.com/TrustVC/trustvc/issues/63)) ([9ddba30](https://github.com/TrustVC/trustvc/commit/9ddba300f4f240d9ec2508528bee791e481d8c04)) +- update package ([#62](https://github.com/TrustVC/trustvc/issues/62)) ([f4f3942](https://github.com/TrustVC/trustvc/commit/f4f3942e84325e69c37e652002fa41e970a09ad0)) ## [1.4.9](https://github.com/TrustVC/trustvc/compare/v1.4.8...v1.4.9) (2025-05-15) - ### Bug Fixes -* version bump ([#61](https://github.com/TrustVC/trustvc/issues/61)) ([92c7b0b](https://github.com/TrustVC/trustvc/commit/92c7b0ba2d72fc11e16cd6c2f8d7883e83f73d9e)) - +- version bump ([#61](https://github.com/TrustVC/trustvc/issues/61)) ([92c7b0b](https://github.com/TrustVC/trustvc/commit/92c7b0ba2d72fc11e16cd6c2f8d7883e83f73d9e)) ### Miscellaneous Chores -* export gas station function from tt-utils ([#60](https://github.com/TrustVC/trustvc/issues/60)) ([5e88b9c](https://github.com/TrustVC/trustvc/commit/5e88b9cde81068a7ca183a1498bd284f928a092a)) +- export gas station function from tt-utils ([#60](https://github.com/TrustVC/trustvc/issues/60)) ([5e88b9c](https://github.com/TrustVC/trustvc/commit/5e88b9cde81068a7ca183a1498bd284f928a092a)) ## [1.4.8](https://github.com/TrustVC/trustvc/compare/v1.4.7...v1.4.8) (2025-05-14) - ### Bug Fixes -* add qrcode functionality to document builder ([#59](https://github.com/TrustVC/trustvc/issues/59)) ([b217e85](https://github.com/TrustVC/trustvc/commit/b217e85e45a2879800cbf44b1de01d9f5f20ebce)) - +- add qrcode functionality to document builder ([#59](https://github.com/TrustVC/trustvc/issues/59)) ([b217e85](https://github.com/TrustVC/trustvc/commit/b217e85e45a2879800cbf44b1de01d9f5f20ebce)) ### Miscellaneous Chores -* add astrontestnet testcase ([#58](https://github.com/TrustVC/trustvc/issues/58)) ([48e82cb](https://github.com/TrustVC/trustvc/commit/48e82cbb62c6188e8dda86e35d431b308647a03c)) +- add astrontestnet testcase ([#58](https://github.com/TrustVC/trustvc/issues/58)) ([48e82cb](https://github.com/TrustVC/trustvc/commit/48e82cbb62c6188e8dda86e35d431b308647a03c)) ## [1.4.7](https://github.com/TrustVC/trustvc/compare/v1.4.6...v1.4.7) (2025-04-22) - ### Bug Fixes -* update checkDidWebResolve function to use queryDidDocument ([#57](https://github.com/TrustVC/trustvc/issues/57)) ([da975ed](https://github.com/TrustVC/trustvc/commit/da975eda1475390ed41a72926b24308c1fb59c6c)) +- update checkDidWebResolve function to use queryDidDocument ([#57](https://github.com/TrustVC/trustvc/issues/57)) ([da975ed](https://github.com/TrustVC/trustvc/commit/da975eda1475390ed41a72926b24308c1fb59c6c)) ## [1.4.6](https://github.com/TrustVC/trustvc/compare/v1.4.5...v1.4.6) (2025-04-22) - ### Bug Fixes -* endorsement chain initial event remarks error ([#56](https://github.com/TrustVC/trustvc/issues/56)) ([1fd5b4c](https://github.com/TrustVC/trustvc/commit/1fd5b4c1e2794e5cd6748c26ab73f4099b110ffd)) +- endorsement chain initial event remarks error ([#56](https://github.com/TrustVC/trustvc/issues/56)) ([1fd5b4c](https://github.com/TrustVC/trustvc/commit/1fd5b4c1e2794e5cd6748c26ab73f4099b110ffd)) ## [1.4.5](https://github.com/TrustVC/trustvc/compare/v1.4.4...v1.4.5) (2025-04-10) - ### Bug Fixes -* prevent comments from being removed, for jsdocs ([#55](https://github.com/TrustVC/trustvc/issues/55)) ([82a4208](https://github.com/TrustVC/trustvc/commit/82a4208b38487c5c54093e65f0726cc14efcd308)) +- prevent comments from being removed, for jsdocs ([#55](https://github.com/TrustVC/trustvc/issues/55)) ([82a4208](https://github.com/TrustVC/trustvc/commit/82a4208b38487c5c54093e65f0726cc14efcd308)) ## [1.4.4](https://github.com/TrustVC/trustvc/compare/v1.4.3...v1.4.4) (2025-04-10) - ### Bug Fixes -* update w3cIssuerIdentity to use documentLoader ([#54](https://github.com/TrustVC/trustvc/issues/54)) ([8477bfc](https://github.com/TrustVC/trustvc/commit/8477bfc48214523de057cc9d047abe15886e1321)) +- update w3cIssuerIdentity to use documentLoader ([#54](https://github.com/TrustVC/trustvc/issues/54)) ([8477bfc](https://github.com/TrustVC/trustvc/commit/8477bfc48214523de057cc9d047abe15886e1321)) ## [1.4.3](https://github.com/TrustVC/trustvc/compare/v1.4.2...v1.4.3) (2025-04-09) - ### Bug Fixes -* allow getDocumentLoader for verifyDocument ([#53](https://github.com/TrustVC/trustvc/issues/53)) ([8a4afc4](https://github.com/TrustVC/trustvc/commit/8a4afc44bcf3ca45a9bc82d7a6838e7df432200c)) +- allow getDocumentLoader for verifyDocument ([#53](https://github.com/TrustVC/trustvc/issues/53)) ([8a4afc4](https://github.com/TrustVC/trustvc/commit/8a4afc44bcf3ca45a9bc82d7a6838e7df432200c)) ## [1.4.2](https://github.com/TrustVC/trustvc/compare/v1.4.1...v1.4.2) (2025-04-08) - ### Bug Fixes -* add getDocumentOwner ([#52](https://github.com/TrustVC/trustvc/issues/52)) ([ac37a05](https://github.com/TrustVC/trustvc/commit/ac37a053f381128cd180b2d11adfd38c3f24014c)) +- add getDocumentOwner ([#52](https://github.com/TrustVC/trustvc/issues/52)) ([ac37a05](https://github.com/TrustVC/trustvc/commit/ac37a053f381128cd180b2d11adfd38c3f24014c)) ## [1.4.1](https://github.com/TrustVC/trustvc/compare/v1.4.0...v1.4.1) (2025-04-08) - ### Bug Fixes -* upgrade package and update getTitleEscrowAddress ([#51](https://github.com/TrustVC/trustvc/issues/51)) ([f0693a1](https://github.com/TrustVC/trustvc/commit/f0693a14d1bcea22d50ab904d45bbf1151c1ceb8)) +- upgrade package and update getTitleEscrowAddress ([#51](https://github.com/TrustVC/trustvc/issues/51)) ([f0693a1](https://github.com/TrustVC/trustvc/commit/f0693a14d1bcea22d50ab904d45bbf1151c1ceb8)) ## [1.4.0](https://github.com/TrustVC/trustvc/compare/v1.3.0...v1.4.0) (2025-04-04) - ### Features -* document builder update ([#50](https://github.com/TrustVC/trustvc/issues/50)) ([a6e2e46](https://github.com/TrustVC/trustvc/commit/a6e2e4617773e3d426d5429f48f1dce8938b2270)) +- document builder update ([#50](https://github.com/TrustVC/trustvc/issues/50)) ([a6e2e46](https://github.com/TrustVC/trustvc/commit/a6e2e4617773e3d426d5429f48f1dce8938b2270)) ## [1.3.0](https://github.com/TrustVC/trustvc/compare/v1.2.11...v1.3.0) (2025-03-27) - ### Features -* document builder for w3c vc ([#47](https://github.com/TrustVC/trustvc/issues/47)) ([fab9c82](https://github.com/TrustVC/trustvc/commit/fab9c8237a072f541474fa778cf9974de6c0769c)) +- document builder for w3c vc ([#47](https://github.com/TrustVC/trustvc/issues/47)) ([fab9c82](https://github.com/TrustVC/trustvc/commit/fab9c8237a072f541474fa778cf9974de6c0769c)) ## [1.2.11](https://github.com/TrustVC/trustvc/compare/v1.2.10...v1.2.11) (2025-03-17) - ### Bug Fixes -* update package ([#46](https://github.com/TrustVC/trustvc/issues/46)) ([c0a5d1c](https://github.com/TrustVC/trustvc/commit/c0a5d1cb8d5988fa803814efdab4b8b90e0760b2)) +- update package ([#46](https://github.com/TrustVC/trustvc/issues/46)) ([c0a5d1c](https://github.com/TrustVC/trustvc/commit/c0a5d1cb8d5988fa803814efdab4b8b90e0760b2)) ## [1.2.10](https://github.com/TrustVC/trustvc/compare/v1.2.9...v1.2.10) (2025-03-13) - ### Bug Fixes -* update exports ([#45](https://github.com/TrustVC/trustvc/issues/45)) ([bf1e165](https://github.com/TrustVC/trustvc/commit/bf1e165e275bb256046e19a5c6796c773856ce37)) +- update exports ([#45](https://github.com/TrustVC/trustvc/issues/45)) ([bf1e165](https://github.com/TrustVC/trustvc/commit/bf1e165e275bb256046e19a5c6796c773856ce37)) ## [1.2.9](https://github.com/TrustVC/trustvc/compare/v1.2.8...v1.2.9) (2025-03-13) - ### Bug Fixes -* update tradetrust utils ([#44](https://github.com/TrustVC/trustvc/issues/44)) ([3058c16](https://github.com/TrustVC/trustvc/commit/3058c16df61047f6ef721533a237769c588a7557)) +- update tradetrust utils ([#44](https://github.com/TrustVC/trustvc/issues/44)) ([3058c16](https://github.com/TrustVC/trustvc/commit/3058c16df61047f6ef721533a237769c588a7557)) ## [1.2.8](https://github.com/TrustVC/trustvc/compare/v1.2.7...v1.2.8) (2025-03-05) - ### Bug Fixes -* supports web3provider for endorsement chain ([#43](https://github.com/TrustVC/trustvc/issues/43)) ([2559f44](https://github.com/TrustVC/trustvc/commit/2559f44f1e432fd6c1784c87dfae00d5bc18e1e0)) +- supports web3provider for endorsement chain ([#43](https://github.com/TrustVC/trustvc/issues/43)) ([2559f44](https://github.com/TrustVC/trustvc/commit/2559f44f1e432fd6c1784c87dfae00d5bc18e1e0)) ## [1.2.7](https://github.com/TrustVC/trustvc/compare/v1.2.6...v1.2.7) (2025-02-12) - ### Bug Fixes -* package ([#42](https://github.com/TrustVC/trustvc/issues/42)) ([7b4d49e](https://github.com/TrustVC/trustvc/commit/7b4d49e71ed5fa8f5733e0d34611cb817ff2c101)) +- package ([#42](https://github.com/TrustVC/trustvc/issues/42)) ([7b4d49e](https://github.com/TrustVC/trustvc/commit/7b4d49e71ed5fa8f5733e0d34611cb817ff2c101)) ## [1.2.6](https://github.com/TrustVC/trustvc/compare/v1.2.5...v1.2.6) (2025-02-11) - ### Bug Fixes -* upgrade pkgs ([#41](https://github.com/TrustVC/trustvc/issues/41)) ([faa0ddc](https://github.com/TrustVC/trustvc/commit/faa0ddccbb6729e6ac43d75826434db597c7e63b)) +- upgrade pkgs ([#41](https://github.com/TrustVC/trustvc/issues/41)) ([faa0ddc](https://github.com/TrustVC/trustvc/commit/faa0ddccbb6729e6ac43d75826434db597c7e63b)) ## [1.2.5](https://github.com/TrustVC/trustvc/compare/v1.2.4...v1.2.5) (2025-02-11) - ### Bug Fixes -* ethers error ([#40](https://github.com/TrustVC/trustvc/issues/40)) ([f41bebc](https://github.com/TrustVC/trustvc/commit/f41bebc1604e693ec72a727fa587332ee7c0e522)) +- ethers error ([#40](https://github.com/TrustVC/trustvc/issues/40)) ([f41bebc](https://github.com/TrustVC/trustvc/commit/f41bebc1604e693ec72a727fa587332ee7c0e522)) ## [1.2.4](https://github.com/TrustVC/trustvc/compare/v1.2.3...v1.2.4) (2025-02-05) - ### Bug Fixes -* upgrade packages ([#37](https://github.com/TrustVC/trustvc/issues/37)) ([35f26c7](https://github.com/TrustVC/trustvc/commit/35f26c71f029a73cb9498c7fb2201d96c19f432e)) +- upgrade packages ([#37](https://github.com/TrustVC/trustvc/issues/37)) ([35f26c7](https://github.com/TrustVC/trustvc/commit/35f26c71f029a73cb9498c7fb2201d96c19f432e)) ## [1.2.3](https://github.com/TrustVC/trustvc/compare/v1.2.2...v1.2.3) (2025-02-04) - ### Bug Fixes -* upgrade pkgs and migration function from website ([#36](https://github.com/TrustVC/trustvc/issues/36)) ([6936e71](https://github.com/TrustVC/trustvc/commit/6936e719d9878957f52764af3e4528273afb7bd3)) +- upgrade pkgs and migration function from website ([#36](https://github.com/TrustVC/trustvc/issues/36)) ([6936e71](https://github.com/TrustVC/trustvc/commit/6936e719d9878957f52764af3e4528273afb7bd3)) ## [1.2.2](https://github.com/TrustVC/trustvc/compare/v1.2.1...v1.2.2) (2025-01-28) - ### Bug Fixes -* upgrade pkgs ([#35](https://github.com/TrustVC/trustvc/issues/35)) ([bf4d412](https://github.com/TrustVC/trustvc/commit/bf4d4129d756bfd3a406688117fa8a60d3bc7cd4)) +- upgrade pkgs ([#35](https://github.com/TrustVC/trustvc/issues/35)) ([bf4d412](https://github.com/TrustVC/trustvc/commit/bf4d4129d756bfd3a406688117fa8a60d3bc7cd4)) ## [1.2.1](https://github.com/TrustVC/trustvc/compare/v1.2.0...v1.2.1) (2025-01-09) - ### Bug Fixes -* import error ([#34](https://github.com/TrustVC/trustvc/issues/34)) ([1450333](https://github.com/TrustVC/trustvc/commit/145033337e40d1c9fa54933829a095eb83bbe465)) +- import error ([#34](https://github.com/TrustVC/trustvc/issues/34)) ([1450333](https://github.com/TrustVC/trustvc/commit/145033337e40d1c9fa54933829a095eb83bbe465)) ## [1.2.0](https://github.com/TrustVC/trustvc/compare/v1.1.3...v1.2.0) (2025-01-09) - ### Features -* add w3cEmptyCredentialStatus fragment ([#33](https://github.com/TrustVC/trustvc/issues/33)) ([76a3098](https://github.com/TrustVC/trustvc/commit/76a3098427cd00dc4102c3850475ce4828adaed2)) +- add w3cEmptyCredentialStatus fragment ([#33](https://github.com/TrustVC/trustvc/issues/33)) ([76a3098](https://github.com/TrustVC/trustvc/commit/76a3098427cd00dc4102c3850475ce4828adaed2)) ## [1.1.3](https://github.com/TrustVC/trustvc/compare/v1.1.2...v1.1.3) (2025-01-07) - ### Bug Fixes -* upgrade package ([#32](https://github.com/TrustVC/trustvc/issues/32)) ([4cb0ac0](https://github.com/TrustVC/trustvc/commit/4cb0ac0ac0cd4aaa2d16ba639f56001143ab45e9)) +- upgrade package ([#32](https://github.com/TrustVC/trustvc/issues/32)) ([4cb0ac0](https://github.com/TrustVC/trustvc/commit/4cb0ac0ac0cd4aaa2d16ba639f56001143ab45e9)) ## [1.1.2](https://github.com/TrustVC/trustvc/compare/v1.1.1...v1.1.2) (2025-01-03) - ### Bug Fixes -* exports and package upgrade ([#31](https://github.com/TrustVC/trustvc/issues/31)) ([b64b375](https://github.com/TrustVC/trustvc/commit/b64b375a45c662ee841dc9d20ee69d5ebc6cae21)) +- exports and package upgrade ([#31](https://github.com/TrustVC/trustvc/issues/31)) ([b64b375](https://github.com/TrustVC/trustvc/commit/b64b375a45c662ee841dc9d20ee69d5ebc6cae21)) ## [1.1.1](https://github.com/TrustVC/trustvc/compare/v1.1.0...v1.1.1) (2024-12-23) - ### Bug Fixes -* imports ([#29](https://github.com/TrustVC/trustvc/issues/29)) ([6cb78d5](https://github.com/TrustVC/trustvc/commit/6cb78d5fbb521ab2b514a0da0ad7ed1efd224d74)) +- imports ([#29](https://github.com/TrustVC/trustvc/issues/29)) ([6cb78d5](https://github.com/TrustVC/trustvc/commit/6cb78d5fbb521ab2b514a0da0ad7ed1efd224d74)) ## [1.1.0](https://github.com/TrustVC/trustvc/compare/v1.0.5...v1.1.0) (2024-12-19) - ### Features -* endorsement chain ([#27](https://github.com/TrustVC/trustvc/issues/27)) ([58c184a](https://github.com/TrustVC/trustvc/commit/58c184a2d1694978e6dd8a55c85c86aa7a9b1212)) +- endorsement chain ([#27](https://github.com/TrustVC/trustvc/issues/27)) ([58c184a](https://github.com/TrustVC/trustvc/commit/58c184a2d1694978e6dd8a55c85c86aa7a9b1212)) ## [1.0.5](https://github.com/TrustVC/trustvc/compare/v1.0.4...v1.0.5) (2024-12-17) - ### Bug Fixes -* credential status to verify set ([#26](https://github.com/TrustVC/trustvc/issues/26)) ([b869b9e](https://github.com/TrustVC/trustvc/commit/b869b9ed66e7f41e00c17efabf6990796ee9121f)) +- credential status to verify set ([#26](https://github.com/TrustVC/trustvc/issues/26)) ([b869b9e](https://github.com/TrustVC/trustvc/commit/b869b9ed66e7f41e00c17efabf6990796ee9121f)) ## [1.0.4](https://github.com/TrustVC/trustvc/compare/v1.0.3...v1.0.4) (2024-12-13) - ### Bug Fixes -* add imports ([#25](https://github.com/TrustVC/trustvc/issues/25)) ([3ea8b9f](https://github.com/TrustVC/trustvc/commit/3ea8b9f610ce93f9d86792a4951c09d3a2269450)) +- add imports ([#25](https://github.com/TrustVC/trustvc/issues/25)) ([3ea8b9f](https://github.com/TrustVC/trustvc/commit/3ea8b9f610ce93f9d86792a4951c09d3a2269450)) ## [1.0.3](https://github.com/TrustVC/trustvc/compare/v1.0.2...v1.0.3) (2024-12-06) - ### Bug Fixes -* update w3c package ([#24](https://github.com/TrustVC/trustvc/issues/24)) ([26d4371](https://github.com/TrustVC/trustvc/commit/26d4371fe0dbb477ed112240be2db7fa13c39a95)) +- update w3c package ([#24](https://github.com/TrustVC/trustvc/issues/24)) ([26d4371](https://github.com/TrustVC/trustvc/commit/26d4371fe0dbb477ed112240be2db7fa13c39a95)) ## [1.0.2](https://github.com/TrustVC/trustvc/compare/v1.0.1...v1.0.2) (2024-12-06) - ### Bug Fixes -* update imports ([#23](https://github.com/TrustVC/trustvc/issues/23)) ([cd5edd1](https://github.com/TrustVC/trustvc/commit/cd5edd1d1718c78273d8c314f542f1ada3e73cd2)) +- update imports ([#23](https://github.com/TrustVC/trustvc/issues/23)) ([cd5edd1](https://github.com/TrustVC/trustvc/commit/cd5edd1d1718c78273d8c314f542f1ada3e73cd2)) ## [1.0.1](https://github.com/TrustVC/trustvc/compare/v1.0.0...v1.0.1) (2024-12-06) - ### Bug Fixes -* update package dependencies ([#22](https://github.com/TrustVC/trustvc/issues/22)) ([92a9795](https://github.com/TrustVC/trustvc/commit/92a97958f06ce862f8e74138d22de60b2f85b2f6)) - +- update package dependencies ([#22](https://github.com/TrustVC/trustvc/issues/22)) ([92a9795](https://github.com/TrustVC/trustvc/commit/92a97958f06ce862f8e74138d22de60b2f85b2f6)) ### Miscellaneous Chores -* update readme ([#21](https://github.com/TrustVC/trustvc/issues/21)) ([b6f0eb3](https://github.com/TrustVC/trustvc/commit/b6f0eb305f26843307592d0efa26a22d05091022)) +- update readme ([#21](https://github.com/TrustVC/trustvc/issues/21)) ([b6f0eb3](https://github.com/TrustVC/trustvc/commit/b6f0eb305f26843307592d0efa26a22d05091022)) ## [1.0.0](https://github.com/TrustVC/trustvc/compare/v0.0.0...v1.0.0) (2024-12-02) - ### ⚠ BREAKING CHANGES -* v1 release +- v1 release ### Features -* breaking change: v1 release ([#19](https://github.com/TrustVC/trustvc/issues/19)) ([2b563e9](https://github.com/TrustVC/trustvc/commit/2b563e9766598aec12127b36260e2a8488932c3a)) - +- breaking change: v1 release ([#19](https://github.com/TrustVC/trustvc/issues/19)) ([2b563e9](https://github.com/TrustVC/trustvc/commit/2b563e9766598aec12127b36260e2a8488932c3a)) ### Miscellaneous Chores -* update ci ([6bcd602](https://github.com/TrustVC/trustvc/commit/6bcd60227e99ab170eed90ab6b3cccca34ed38aa)) +- update ci ([6bcd602](https://github.com/TrustVC/trustvc/commit/6bcd60227e99ab170eed90ab6b3cccca34ed38aa)) diff --git a/src/__tests__/token-registry-functions/fixtures.ts b/src/__tests__/token-registry-functions/fixtures.ts new file mode 100644 index 0000000..0745a5e --- /dev/null +++ b/src/__tests__/token-registry-functions/fixtures.ts @@ -0,0 +1,117 @@ +import { vi } from 'vitest'; +import { ethers as ethersV5 } from 'ethers'; +import { JsonRpcProvider as JsonRpcProviderV6 } from 'ethersV6'; + +vi.mock('src/core', () => ({ + encrypt: vi.fn(() => 'encrypted_remarks'), + getTitleEscrowAddress: vi.fn(() => Promise.resolve('0xv5contract')), + isTitleEscrowVersion: vi.fn(() => Promise.resolve(true)), + TitleEscrowInterface: { + V4: '0xTitleEscrowIdV4', + V5: '0xTitleEscrowIdV5', + }, +})); + +vi.mock('src/token-registry-v5', () => { + return { + v5Contracts: { + TitleEscrow__factory: { + connect: vi.fn(() => mockV5TitleEscrowContract), + }, + TradeTrustToken__factory: { + connect: vi.fn(() => mockV5TradeTrustTokenContract), + }, + TitleEscrowFactory__factory: { + connect: vi.fn(() => mockV5TitleEscrowFactoryContract), + }, + }, + v5SupportInterfaceIds: { + TitleEscrow: '0xTitleEscrowIdV5', + TradeTrustTokenMintable: '0xTradeTrustTokenMintableIdV5', + }, + }; +}); + +vi.mock('src/token-registry-v4', () => { + return { + v4Contracts: { + TitleEscrow__factory: { + connect: vi.fn(() => mockV4TitleEscrowContract), + }, + TradeTrustToken__factory: { + connect: vi.fn(() => mockV4TradeTrustTokenContract), + }, + TitleEscrowFactory__factory: { + connect: vi.fn(() => mockV4TitleEscrowFactoryContract), + }, + v4SupportInterfaceIds: { + TitleEscrow: '0xTitleEscrowIdV4', + TradeTrustTokenMintable: '0xTradeTrustTokenMintableIdV4', + }, + }, + }; +}); + +export const mockV5TitleEscrowFactoryContract = { + callStatic: { + getEscrowAddress: vi.fn(), + }, + getEscrowAddress: vi.fn(() => Promise.resolve('0xV5titleescrow')), +}; + +export const mockV5TradeTrustTokenContract = { + supportsInterface: vi.fn(), + titleEscrowFactory: vi.fn(() => Promise.resolve('0xV5titleescrowfactory')), +}; + +export const mockV5TitleEscrowContract = { + supportsInterface: vi.fn(), + callStatic: { + transferHolder: vi.fn(), + transferBeneficiary: vi.fn(), + transferOwners: vi.fn(), + nominate: vi.fn(), + rejectTransferHolder: vi.fn(), + rejectTransferBeneficiary: vi.fn(), + rejectTransferOwners: vi.fn(), + }, + transferHolder: vi.fn(() => Promise.resolve('v5_transfer_holder_tx_hash')), + transferBeneficiary: vi.fn(() => Promise.resolve('v5_transfer_beneficiary_tx_hash')), + transferOwners: vi.fn(() => Promise.resolve('v5_transfer_owners_tx_hash')), + nominate: vi.fn(() => Promise.resolve('v5_nominate_tx_hash')), + holder: vi.fn(() => Promise.resolve('0xcurrent_holder')), + beneficiary: vi.fn(() => Promise.resolve('0xcurrent_beneficiary')), + rejectTransferHolder: vi.fn(() => Promise.resolve('v5_reject_transfer_holder_tx_hash')), + rejectTransferBeneficiary: vi.fn(() => Promise.resolve('v5_reject_transfer_beneficiary_tx_hash')), + rejectTransferOwners: vi.fn(() => Promise.resolve('v5_reject_transfer_owners_tx_hash')), +}; + +export const mockV4TitleEscrowContract = { + callStatic: { + transferHolder: vi.fn(), + transferBeneficiary: vi.fn(), + transferOwners: vi.fn(), + nominate: vi.fn(), + }, + transferHolder: vi.fn(() => Promise.resolve('v4_transfer_holder_tx_hash')), + transferBeneficiary: vi.fn(() => Promise.resolve('v4_transfer_beneficiary_tx_hash')), + transferOwners: vi.fn(() => Promise.resolve('v4_transfer_owners_tx_hash')), + nominate: vi.fn(() => Promise.resolve('v4_nominate_tx_hash')), + holder: vi.fn(() => Promise.resolve('0xcurrent_holder')), + beneficiary: vi.fn(() => Promise.resolve('0xcurrent_beneficiary')), +}; +export const mockV4TitleEscrowFactoryContract = { + callStatic: { + getEscrowAddress: vi.fn(), + }, + getAddress: vi.fn(() => Promise.resolve('0xV4titleescrow')), +}; + +export const mockV4TradeTrustTokenContract = { + titleEscrowFactory: vi.fn(() => Promise.resolve('0xV4titleescrowfactory')), +}; + +export const PRIVATE_KEY = '0x59c6995e998f97a5a004497e5f1ebce0c16828d44b3f8d0bfa3a89d271d5b6b9'; // random local key + +export const providerV5 = new ethersV5.providers.JsonRpcProvider(); +export const providerV6 = new JsonRpcProviderV6(); diff --git a/src/__tests__/token-registry-functions/rejectTransfers.test.ts b/src/__tests__/token-registry-functions/rejectTransfers.test.ts new file mode 100644 index 0000000..9af0fcc --- /dev/null +++ b/src/__tests__/token-registry-functions/rejectTransfers.test.ts @@ -0,0 +1,438 @@ +import './fixtures.js'; +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { ethers as ethersV5, Wallet as WalletV5 } from 'ethers'; +import { ethers as ethersV6, Network, Wallet as WalletV6 } from 'ethersV6'; +import * as coreModule from 'src/core'; +import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils'; +import { + rejectTransferBeneficiary, + rejectTransferHolder, + rejectTransferOwners, +} from 'src/token-registry-functions/rejectTransfers'; +import { mockV5TitleEscrowContract, PRIVATE_KEY, providerV5, providerV6 } from './fixtures'; +import { ProviderInfo } from 'src/token-registry-functions/types.js'; + +const providers: ProviderInfo[] = [ + { + Provider: providerV5, + ethersVersion: 'v5', + titleEscrowVersion: 'v5', + }, + { + Provider: providerV6, + ethersVersion: 'v6', + titleEscrowVersion: 'v5', + }, +]; + +describe.each(providers)( + 'Reject Transfers', + async ({ Provider, ethersVersion, titleEscrowVersion }) => { + const mockTokenRegistryAddress = '0xTokenRegistry'; + const mockTokenId = '0xTokenId'; + const mockTitleEscrowAddress = '0xTitleEscrow'; + const mockRemarks = 'Rejection remarks'; + const mockChainId = CHAIN_ID.local; + const mockEncryptedRemarks = '0xencryptedRemarks'; + + let wallet: ethersV5.Wallet | ethersV6.Wallet; + beforeEach(() => { + // Reset all mocks before each test + vi.clearAllMocks(); + + if (ethersVersion === 'v5') { + wallet = new WalletV5(PRIVATE_KEY, Provider as any) as ethersV5.Wallet; + // wallet = { + // ...wallet, + // address: '0xcurrent_holder', + // getChainId: vi.fn().mockResolvedValue(CHAIN_ID.mainnet as unknown as number), + // } as any; + vi.spyOn(wallet, 'getChainId').mockResolvedValue(CHAIN_ID.mainnet as unknown as number); + } else { + wallet = new WalletV6(PRIVATE_KEY, Provider as any); + vi.spyOn(Provider, 'getNetwork').mockResolvedValue({ + chainId: CHAIN_ID.mainnet, + } as unknown as Network); + // vi.spyOn(wallet, 'getAddress').mockResolvedValue('0xcurrent_holder'); + } + + // Mock core functions + vi.spyOn(coreModule, 'getTitleEscrowAddress').mockResolvedValue(mockTitleEscrowAddress); + vi.spyOn(coreModule, 'isTitleEscrowVersion').mockResolvedValue(true); + vi.spyOn(coreModule, 'encrypt').mockReturnValue(mockEncryptedRemarks.slice(2)); + + // Mock contract calls + }); + describe(`Reject Transfers Holder with ethers version ${ethersVersion}`, () => { + it('should reject transfer holder with signer and all required parameters', async () => { + const result = await rejectTransferHolder( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ); + + expect(result).toEqual('v5_reject_transfer_holder_tx_hash'); + }); + + it('should reject transfer holder when titleEscrowAddress is provided', async () => { + const result = await rejectTransferHolder( + { + titleEscrowAddress: mockTitleEscrowAddress, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ); + + expect(result).toEqual('v5_reject_transfer_holder_tx_hash'); + expect(coreModule.getTitleEscrowAddress).not.toHaveBeenCalled(); + }); + + it('should reject transfer holder without remarks', async () => { + const result = await rejectTransferHolder( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + {}, + { chainId: mockChainId }, + ); + + expect(result).toEqual('v5_reject_transfer_holder_tx_hash'); + expect(coreModule.encrypt).not.toHaveBeenCalled(); + }); + + it('should throw error when tokenRegistryAddress is missing', async () => { + vi.mocked(coreModule.getTitleEscrowAddress).mockResolvedValue(undefined); + await expect( + rejectTransferHolder( + { + tokenId: mockTokenId, + } as any, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Token registry address is required'); + }); + + it('should throw error when provider is missing', async () => { + const signerWithoutProvider = new WalletV5('0x'.padEnd(66, '1')); + + await expect( + rejectTransferHolder( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + signerWithoutProvider, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Provider is required'); + }); + + it('should throw error when title escrow is not V5', async () => { + vi.spyOn(coreModule, 'isTitleEscrowVersion').mockResolvedValue(false); + + await expect( + rejectTransferHolder( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Only Token Registry V5 is supported'); + }); + + it('should throw error when callStatic fails', async () => { + mockV5TitleEscrowContract.callStatic.rejectTransferHolder.mockRejectedValue( + new Error('Simulated failure'), + ); + + await expect( + rejectTransferHolder( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Pre-check (callStatic) for transferHolder failed'); + mockV5TitleEscrowContract.callStatic.rejectTransferHolder = vi.fn(); + }); + + it('should use explicit titleEscrowVersion when provided', async () => { + await rejectTransferHolder( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id', titleEscrowVersion }, + ); + + expect(coreModule.isTitleEscrowVersion).not.toHaveBeenCalled(); + }); + }); + + describe(`Reject Transfers Beneficiary with ethers version ${ethersVersion}`, () => { + it('should reject transfer beneficiary with signer and all required parameters', async () => { + const result = await rejectTransferBeneficiary( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ); + + expect(result).toEqual('v5_reject_transfer_beneficiary_tx_hash'); + }); + + it('should reject transfer beneficiary when titleEscrowAddress is provided', async () => { + const result = await rejectTransferBeneficiary( + { + titleEscrowAddress: mockTitleEscrowAddress, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ); + + expect(result).toEqual('v5_reject_transfer_beneficiary_tx_hash'); + expect(coreModule.getTitleEscrowAddress).not.toHaveBeenCalled(); + }); + + it('should reject transfer beneficiary without remarks', async () => { + const result = await rejectTransferBeneficiary( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + {}, + { chainId: mockChainId }, + ); + + expect(result).toEqual('v5_reject_transfer_beneficiary_tx_hash'); + expect(coreModule.encrypt).not.toHaveBeenCalled(); + }); + + it('should throw error when tokenRegistryAddress is missing', async () => { + vi.mocked(coreModule.getTitleEscrowAddress).mockResolvedValue(undefined); + await expect( + rejectTransferBeneficiary( + { + tokenId: mockTokenId, + } as any, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Token registry address is required'); + }); + + it('should throw error when provider is missing', async () => { + const signerWithoutProvider = new WalletV5('0x'.padEnd(66, '1')); + + await expect( + rejectTransferBeneficiary( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + signerWithoutProvider, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Provider is required'); + }); + + it('should throw error when title escrow is not V5', async () => { + vi.spyOn(coreModule, 'isTitleEscrowVersion').mockResolvedValue(false); + + await expect( + rejectTransferBeneficiary( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Only Token Registry V5 is supported'); + }); + + it('should throw error when callStatic fails', async () => { + mockV5TitleEscrowContract.callStatic.rejectTransferBeneficiary.mockRejectedValue( + new Error('Simulated failure'), + ); + + await expect( + rejectTransferBeneficiary( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Pre-check (callStatic) for transferHolder failed'); + mockV5TitleEscrowContract.callStatic.rejectTransferBeneficiary = vi.fn(); + }); + + it('should use explicit titleEscrowVersion when provided', async () => { + await rejectTransferBeneficiary( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id', titleEscrowVersion }, + ); + + expect(coreModule.isTitleEscrowVersion).not.toHaveBeenCalled(); + }); + }); + + describe(`Reject Transfers Owners with ethers version ${ethersVersion}`, () => { + it('should reject transfer beneficiary with signer and all required parameters', async () => { + const result = await rejectTransferOwners( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ); + + expect(result).toEqual('v5_reject_transfer_owners_tx_hash'); + }); + + it('should reject transfer beneficiary when titleEscrowAddress is provided', async () => { + const result = await rejectTransferOwners( + { + titleEscrowAddress: mockTitleEscrowAddress, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ); + + expect(result).toEqual('v5_reject_transfer_owners_tx_hash'); + expect(coreModule.getTitleEscrowAddress).not.toHaveBeenCalled(); + }); + + it('should reject transfer beneficiary without remarks', async () => { + const result = await rejectTransferOwners( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + {}, + { chainId: mockChainId }, + ); + + expect(result).toEqual('v5_reject_transfer_owners_tx_hash'); + expect(coreModule.encrypt).not.toHaveBeenCalled(); + }); + + it('should throw error when tokenRegistryAddress is missing', async () => { + vi.mocked(coreModule.getTitleEscrowAddress).mockResolvedValue(undefined); + await expect( + rejectTransferOwners( + { + tokenId: mockTokenId, + } as any, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Token registry address is required'); + }); + + it('should throw error when provider is missing', async () => { + const signerWithoutProvider = new WalletV5('0x'.padEnd(66, '1')); + + await expect( + rejectTransferOwners( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + signerWithoutProvider, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Provider is required'); + }); + + it('should throw error when title escrow is not V5', async () => { + vi.spyOn(coreModule, 'isTitleEscrowVersion').mockResolvedValue(false); + + await expect( + rejectTransferOwners( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Only Token Registry V5 is supported'); + }); + + it('should throw error when callStatic fails', async () => { + mockV5TitleEscrowContract.callStatic.rejectTransferOwners.mockRejectedValue( + new Error('Simulated failure'), + ); + + await expect( + rejectTransferOwners( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id' }, + ), + ).rejects.toThrow('Pre-check (callStatic) for transferHolder failed'); + mockV5TitleEscrowContract.callStatic.rejectTransferOwners = vi.fn(); + }); + + it('should use explicit titleEscrowVersion when provided', async () => { + await rejectTransferOwners( + { + tokenRegistryAddress: mockTokenRegistryAddress, + tokenId: mockTokenId, + }, + wallet, + { remarks: mockRemarks }, + { chainId: mockChainId, id: 'encryption-id', titleEscrowVersion }, + ); + + expect(coreModule.isTitleEscrowVersion).not.toHaveBeenCalled(); + }); + }); + }, +); diff --git a/src/__tests__/token-registry-functions/transfers.test.ts b/src/__tests__/token-registry-functions/transfers.test.ts index c87447c..5f1d6f4 100644 --- a/src/__tests__/token-registry-functions/transfers.test.ts +++ b/src/__tests__/token-registry-functions/transfers.test.ts @@ -1,11 +1,7 @@ +import './fixtures.js'; import { describe, it, expect, beforeEach, vi } from 'vitest'; import { ethers as ethersV5, Wallet as WalletV5 } from 'ethers'; -import { - ethers as ethersV6, - JsonRpcProvider as JsonRpcProviderV6, - Network, - Wallet as WalletV6, -} from 'ethersV6'; +import { ethers as ethersV6, Network, Wallet as WalletV6 } from 'ethersV6'; import * as coreModule from 'src/core'; import { encrypt } from 'src/core'; import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils'; @@ -15,122 +11,14 @@ import { transferOwners, nominate, } from 'src/token-registry-functions'; - -// Mocks - -vi.mock('src/core', () => ({ - encrypt: vi.fn(() => 'encrypted_remarks'), - getTitleEscrowAddress: vi.fn(() => Promise.resolve('0xv5contract')), - isTitleEscrowVersion: vi.fn(() => Promise.resolve(true)), - TitleEscrowInterface: { - V4: '0xTitleEscrowIdV4', - V5: '0xTitleEscrowIdV5', - }, -})); - -vi.mock('src/token-registry-v5', () => { - return { - v5Contracts: { - TitleEscrow__factory: { - connect: vi.fn(() => mockV5TitleEscrowContract), - }, - TradeTrustToken__factory: { - connect: vi.fn(() => mockV5TradeTrustTokenContract), - }, - TitleEscrowFactory__factory: { - connect: vi.fn(() => mockV5TitleEscrowFactoryContract), - }, - }, - v5SupportInterfaceIds: { - TitleEscrow: '0xTitleEscrowIdV5', - TradeTrustTokenMintable: '0xTradeTrustTokenMintableIdV5', - }, - }; -}); - -vi.mock('src/token-registry-v4', () => { - return { - v4Contracts: { - TitleEscrow__factory: { - connect: vi.fn(() => mockV4TitleEscrowContract), - }, - TradeTrustToken__factory: { - connect: vi.fn(() => mockV4TradeTrustTokenContract), - }, - TitleEscrowFactory__factory: { - connect: vi.fn(() => mockV4TitleEscrowFactoryContract), - }, - v4SupportInterfaceIds: { - TitleEscrow: '0xTitleEscrowIdV4', - TradeTrustTokenMintable: '0xTradeTrustTokenMintableIdV4', - }, - }, - }; -}); - -const mockV5TitleEscrowFactoryContract = { - callStatic: { - getEscrowAddress: vi.fn(), - }, - getEscrowAddress: vi.fn(() => Promise.resolve('0xV5titleescrow')), -}; - -const mockV5TradeTrustTokenContract = { - supportsInterface: vi.fn(), - titleEscrowFactory: vi.fn(() => Promise.resolve('0xV5titleescrowfactory')), -}; - -const mockV5TitleEscrowContract = { - supportsInterface: vi.fn(), - callStatic: { - transferHolder: vi.fn(), - transferBeneficiary: vi.fn(), - transferOwners: vi.fn(), - nominate: vi.fn(), - }, - transferHolder: vi.fn(() => Promise.resolve('v5_transfer_holder_tx_hash')), - transferBeneficiary: vi.fn(() => Promise.resolve('v5_transfer_beneficiary_tx_hash')), - transferOwners: vi.fn(() => Promise.resolve('v5_transfer_owners_tx_hash')), - nominate: vi.fn(() => Promise.resolve('v5_nominate_tx_hash')), - holder: vi.fn(() => Promise.resolve('0xcurrent_holder')), - beneficiary: vi.fn(() => Promise.resolve('0xcurrent_beneficiary')), -}; - -const mockV4TitleEscrowContract = { - callStatic: { - transferHolder: vi.fn(), - transferBeneficiary: vi.fn(), - transferOwners: vi.fn(), - nominate: vi.fn(), - }, - transferHolder: vi.fn(() => Promise.resolve('v4_transfer_holder_tx_hash')), - transferBeneficiary: vi.fn(() => Promise.resolve('v4_transfer_beneficiary_tx_hash')), - transferOwners: vi.fn(() => Promise.resolve('v4_transfer_owners_tx_hash')), - nominate: vi.fn(() => Promise.resolve('v4_nominate_tx_hash')), - holder: vi.fn(() => Promise.resolve('0xcurrent_holder')), - beneficiary: vi.fn(() => Promise.resolve('0xcurrent_beneficiary')), -}; -const mockV4TitleEscrowFactoryContract = { - callStatic: { - getEscrowAddress: vi.fn(), - }, - getAddress: vi.fn(() => Promise.resolve('0xV4titleescrow')), -}; - -const mockV4TradeTrustTokenContract = { - titleEscrowFactory: vi.fn(() => Promise.resolve('0xV4titleescrowfactory')), -}; - -const PRIVATE_KEY = '0x59c6995e998f97a5a004497e5f1ebce0c16828d44b3f8d0bfa3a89d271d5b6b9'; // random local key - -const providerV5 = new ethersV5.providers.JsonRpcProvider(); -const providerV6 = new JsonRpcProviderV6(); - -interface ProviderInfo { - Provider: typeof providerV5 | typeof providerV6; - ethersVersion: 'v5' | 'v6'; - titleEscrowVersion: 'v4' | 'v5'; -} +import { ProviderInfo } from 'src/token-registry-functions/types'; +import { + mockV4TitleEscrowContract, + mockV5TitleEscrowContract, + PRIVATE_KEY, + providerV5, + providerV6, +} from './fixtures'; const providers: ProviderInfo[] = [ { @@ -189,7 +77,6 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc holderAddress: '0xholder', tokenId: 1, }; - const txHash = isV5TT ? 'v5_transfer_holder_tx_hash' : 'v4_transfer_holder_tx_hash'; it('throws error if titleEscrowAddress is missing ', async () => { @@ -529,7 +416,6 @@ describe.each(providers)('Transfers', async ({ Provider, ethersVersion, titleEsc remarks: '0xencrypted_remarks', } : { newBeneficiaryAddress: '0xbeneficiary', newHolderAddress: '0xholder' }; - const txHash = isV5TT ? 'v5_transfer_owners_tx_hash' : 'v4_transfer_owners_tx_hash'; it('throws error if titleEscrowAddress is missing ', async () => { diff --git a/src/token-registry-functions/index.ts b/src/token-registry-functions/index.ts index 6de95ff..927e270 100644 --- a/src/token-registry-functions/index.ts +++ b/src/token-registry-functions/index.ts @@ -1 +1,2 @@ export * from './transfer'; +export * from './rejectTransfers'; diff --git a/src/token-registry-functions/rejectTransfers.ts b/src/token-registry-functions/rejectTransfers.ts new file mode 100644 index 0000000..a32ef4a --- /dev/null +++ b/src/token-registry-functions/rejectTransfers.ts @@ -0,0 +1,217 @@ +import { + encrypt, + getTitleEscrowAddress, + isTitleEscrowVersion, + TitleEscrowInterface, +} from 'src/core'; +import { v5Contracts } from 'src/token-registry-v5'; +import { Signer as SignerV6 } from 'ethersV6'; +import { ContractTransaction, Signer } from 'ethers'; +import { getTxOptions } from './utils'; +import { ContractOptions, RejectTransferParams, TransactionOptions } from './types'; + +/** + * Rejects the transfer of holder for a title escrow contract. + * @param {ContractOptions} contractOptions - Contract-related options including the token registry address, and optionally, token ID and the title escrow address. + * @param {Signer | SignerV6} signer - Ethers signer (V5 or V6) used to sign and send the transaction. + * @param {RejectTransferParams} params - Contains the `remarks` field which is an optional string that will be encrypted and sent with the transaction. + * @param {TransactionOptions} options - Transfer options including optional `chainId`, `titleEscrowVersion`, `maxFeePerGas`, `maxPriorityFeePerGas`, and an `id` used for encryption. + * @throws error if the title escrow address or signer provider is missing. + * @throws if the version is not V5 compatible. + * @throws if the dry-run (`callStatic`) fails. + * @returns {Promise} The transaction response of the rejectTransferHolder call. + */ +const rejectTransferHolder = async ( + contractOptions: ContractOptions, + signer: Signer | SignerV6, + params: RejectTransferParams, + options: TransactionOptions, +): Promise => { + const { tokenRegistryAddress, tokenId } = contractOptions; + let { titleEscrowAddress } = contractOptions; + const { chainId, maxFeePerGas, maxPriorityFeePerGas, titleEscrowVersion } = options; + + if (!titleEscrowAddress) { + titleEscrowAddress = await getTitleEscrowAddress( + tokenRegistryAddress, + tokenId as string, + signer.provider, + {}, + ); + } + + if (!titleEscrowAddress) throw new Error('Token registry address is required'); + if (!signer.provider) throw new Error('Provider is required'); + const { remarks } = params; + + // Connect V5 contract by default + const titleEscrowContract = v5Contracts.TitleEscrow__factory.connect(titleEscrowAddress, signer); + + const encryptedRemarks = remarks ? `0x${encrypt(remarks, options.id!)}` : '0x'; + + // Detect version if not explicitly provided + let isV5TT = titleEscrowVersion === 'v5'; + if (titleEscrowVersion === undefined) { + isV5TT = await isTitleEscrowVersion({ + titleEscrowAddress, + versionInterface: TitleEscrowInterface.V5, + provider: signer.provider, + }); + } + + if (!isV5TT) { + throw new Error('Only Token Registry V5 is supported'); + } + + // Check callStatic (dry run) + try { + await titleEscrowContract.callStatic.rejectTransferHolder(encryptedRemarks); + } catch (e) { + console.error('callStatic failed:', e); + throw new Error('Pre-check (callStatic) for transferHolder failed'); + } + + const txOptions = await getTxOptions(signer, chainId, maxFeePerGas, maxPriorityFeePerGas); + + // Send the actual transaction + + return await titleEscrowContract.rejectTransferHolder(encryptedRemarks, txOptions); +}; + +/** + * Rejects the transfer of beneficiary for a title escrow contract. + * @param {ContractOptions} contractOptions - Contract-related options including the token registry address, and optionally, token ID and the title escrow address. + * @param {Signer | SignerV6} signer - Ethers signer (V5 or V6) used to sign and send the transaction. + * @param {RejectTransferParams} params - Contains the `remarks` field which is an optional string that will be encrypted and sent with the transaction. + * @param {TransactionOptions} options - Transfer options including optional `chainId`, `titleEscrowVersion`, `maxFeePerGas`, `maxPriorityFeePerGas`, and an `id` used for encryption. + * @throws error if the title escrow address or signer provider is missing. + * @throws error if the version is not V5 compatible. + * @throws error if the dry-run (`callStatic`) fails. + * @returns {Promise} The transaction response of the rejectTransferBeneficiary call. + */ +const rejectTransferBeneficiary = async ( + contractOptions: ContractOptions, + signer: Signer | SignerV6, + params: RejectTransferParams, + options: TransactionOptions, +): Promise => { + const { tokenRegistryAddress, tokenId } = contractOptions; + let { titleEscrowAddress } = contractOptions; + const { chainId, maxFeePerGas, maxPriorityFeePerGas, titleEscrowVersion } = options; + + if (!titleEscrowAddress) { + titleEscrowAddress = await getTitleEscrowAddress( + tokenRegistryAddress, + tokenId as string, + signer.provider, + {}, + ); + } + + if (!titleEscrowAddress) throw new Error('Token registry address is required'); + if (!signer.provider) throw new Error('Provider is required'); + const { remarks } = params; + + // Connect V5 contract by default + const titleEscrowContract = v5Contracts.TitleEscrow__factory.connect(titleEscrowAddress, signer); + + const encryptedRemarks = remarks ? `0x${encrypt(remarks, options.id!)}` : '0x'; + + // Detect version if not explicitly provided + let isV5TT = titleEscrowVersion === 'v5'; + if (titleEscrowVersion === undefined) { + isV5TT = await isTitleEscrowVersion({ + titleEscrowAddress, + versionInterface: TitleEscrowInterface.V5, + provider: signer.provider, + }); + } + + if (!isV5TT) { + throw new Error('Only Token Registry V5 is supported'); + } + + // Check callStatic (dry run) + try { + await titleEscrowContract.callStatic.rejectTransferBeneficiary(encryptedRemarks); + } catch (e) { + console.error('callStatic failed:', e); + throw new Error('Pre-check (callStatic) for transferHolder failed'); + } + + const txOptions = await getTxOptions(signer, chainId, maxFeePerGas, maxPriorityFeePerGas); + + // Send the actual transaction + + return await titleEscrowContract.rejectTransferBeneficiary(encryptedRemarks, txOptions); +}; + +/** + * Rejects the transfer of ownership for a title escrow contract. + * @param {ContractOptions} contractOptions - Contract-related options including the token registry address, and optionally, token ID and the title escrow address. + * @param {Signer | SignerV6} signer - Ethers signer (V5 or V6) used to sign and send the transaction. + * @param {RejectTransferParams} params - Contains the `remarks` field which is an optional string that will be encrypted and sent with the transaction. + * @param {TransactionOptions} options - Transfer options including optional `chainId`, `titleEscrowVersion`, `maxFeePerGas`, `maxPriorityFeePerGas`, and an `id` used for encryption. + * @throws error if the title escrow address or signer provider is missing. + * @throws an error if the version is not V5 compatible. + * @throws an error if the dry-run (`callStatic`) fails. + * @returns {Promise} The transaction response of the rejectTransferOwners call. + */ +const rejectTransferOwners = async ( + contractOptions: ContractOptions, + signer: Signer | SignerV6, + params: RejectTransferParams, + options: TransactionOptions, +): Promise => { + const { tokenRegistryAddress, tokenId } = contractOptions; + let { titleEscrowAddress } = contractOptions; + const { chainId, maxFeePerGas, maxPriorityFeePerGas, titleEscrowVersion } = options; + + if (!titleEscrowAddress) { + titleEscrowAddress = await getTitleEscrowAddress( + tokenRegistryAddress, + tokenId as string, + signer.provider, + {}, + ); + } + + if (!titleEscrowAddress) throw new Error('Token registry address is required'); + if (!signer.provider) throw new Error('Provider is required'); + const { remarks } = params; + + // Connect V5 contract by default + const titleEscrowContract = v5Contracts.TitleEscrow__factory.connect(titleEscrowAddress, signer); + + const encryptedRemarks = remarks ? `0x${encrypt(remarks, options.id!)}` : '0x'; + + // Detect version if not explicitly provided + let isV5TT = titleEscrowVersion === 'v5'; + if (titleEscrowVersion === undefined) { + isV5TT = await isTitleEscrowVersion({ + titleEscrowAddress, + versionInterface: TitleEscrowInterface.V5, + provider: signer.provider, + }); + } + + if (!isV5TT) { + throw new Error('Only Token Registry V5 is supported'); + } + + // Check callStatic (dry run) + try { + await titleEscrowContract.callStatic.rejectTransferOwners(encryptedRemarks); + } catch (e) { + console.error('callStatic failed:', e); + throw new Error('Pre-check (callStatic) for transferHolder failed'); + } + + const txOptions = await getTxOptions(signer, chainId, maxFeePerGas, maxPriorityFeePerGas); + + // Send the actual transaction + + return await titleEscrowContract.rejectTransferOwners(encryptedRemarks, txOptions); +}; + +export { rejectTransferHolder, rejectTransferBeneficiary, rejectTransferOwners }; diff --git a/src/token-registry-functions/transfer.ts b/src/token-registry-functions/transfer.ts index 5247762..7c944d3 100644 --- a/src/token-registry-functions/transfer.ts +++ b/src/token-registry-functions/transfer.ts @@ -1,4 +1,3 @@ -import { CHAIN_ID, SUPPORTED_CHAINS } from '@tradetrust-tt/tradetrust-utils'; import { encrypt, getTitleEscrowAddress, @@ -7,74 +6,44 @@ import { } from 'src/core'; import { v4Contracts } from 'src/token-registry-v4'; import { v5Contracts } from 'src/token-registry-v5'; -import { BigNumberish, Signer as SignerV6 } from 'ethersV6'; -import { BigNumber, Signer } from 'ethers'; -import { isV6EthersProvider } from 'src/utils/ethers'; - -interface TransferHolderParams { - holderAddress: string; - remarks?: string; -} -interface TransferBeneficiaryParams { - newBeneficiaryAddress: string; - remarks?: string; -} -interface NominateParams { - newBeneficiaryAddress: string; - remarks?: string; -} -interface TransferOwnersParams { - newHolderAddress: string; - newBeneficiaryAddress: string; - remarks?: string; -} - -interface TransferOptions { - chainId?: CHAIN_ID; - titleEscrowVersion?: 'v4' | 'v5'; - maxFeePerGas?: BigNumberish | string | number | BigNumber; - maxPriorityFeePerGas?: BigNumberish | string | number | BigNumber; - id?: string; -} - -// 🔍 Handles both Ethers v5 and v6 signer types -const getChainIdSafe = async (signer: SignerV6 | Signer): Promise => { - if (isV6EthersProvider(signer.provider)) { - const network = await (signer as Signer).provider?.getNetwork(); - if (!network?.chainId) throw new Error('Cannot determine chainId: provider is missing'); - return network.chainId; - } - return await (signer as Signer).getChainId(); -}; -// const getSignerAddressSafe = async (signer: SignerV6 | Signer): Promise => { -// if (isV6EthersProvider(signer.provider)) { -// return await (signer as SignerV6).getAddress(); -// } -// return (signer as any).address; -// }; - -type ContractOptions = - | { - titleEscrowAddress: string; // Present — no restrictions on the rest - tokenId?: string | number; - tokenRegistryAddress?: string; - } - | { - titleEscrowAddress?: undefined; // Absent — must provide both below - tokenId: string | number; - tokenRegistryAddress: string; - }; - +import { Signer as SignerV6 } from 'ethersV6'; +import { ContractTransaction, Signer } from 'ethers'; +import { + ContractOptions, + NominateParams, + TransactionOptions, + TransferBeneficiaryParams, + TransferHolderParams, + TransferOwnersParams, +} from './types'; +import { getTxOptions } from './utils'; + +/** + * Transfers holder role of a Title Escrow contract to a new address. + * The caller of this function must be the current holder. + * @param {ContractOptions} contractOptions - Contains `tokenRegistryAddress` and optionally `tokenId` and `titleEscrowAddress`. + * @param {Signer | SignerV6} signer - The signer (ethers v5 or v6) who initiates the transaction. + * @param {TransferHolderParams} params - Object containing `holderAddress` (address to transfer to) and optional `remarks`. + * @param {TransactionOptions} options - Transaction options including: + * - `titleEscrowVersion` (optional): Either "v4" or "v5" + * - `chainId` (optional): Used for gas station lookup + * - `maxFeePerGas` (optional), `maxPriorityFeePerGas` (optional): EIP-1559 gas fee configuration + * - `id` (optional): ID used for encrypting remarks + * @throws If required fields like `titleEscrowAddress` or `signer.provider` are missing. + * @throws If the version is unsupported (neither v4 nor v5). + * @throws If the dry-run via `callStatic` fails. + * @returns {Promise} The transaction response for `transferHolder`. + */ const transferHolder = async ( contractOptions: ContractOptions, signer: Signer | SignerV6, params: TransferHolderParams, - options: TransferOptions, -) => { + options: TransactionOptions, +): Promise => { const { tokenRegistryAddress, tokenId } = contractOptions; const { titleEscrowVersion } = options; let { titleEscrowAddress } = contractOptions; - let { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; + const { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; let isV5TT = titleEscrowVersion === 'v5'; let isV4TT = titleEscrowVersion === 'v4'; @@ -153,18 +122,8 @@ const transferHolder = async ( } // If gas values are missing, query gas station if available - if (!maxFeePerGas || !maxPriorityFeePerGas) { - chainId = chainId ?? ((await getChainIdSafe(signer)) as unknown as CHAIN_ID); - const gasStation = SUPPORTED_CHAINS[chainId]?.gasStation; - - if (gasStation) { - const gasFees = await gasStation(); - maxFeePerGas = gasFees?.maxFeePerGas ?? 0; - maxPriorityFeePerGas = gasFees?.maxPriorityFeePerGas ?? 0; - } - } - const txOptions = - maxFeePerGas && maxPriorityFeePerGas ? { maxFeePerGas, maxPriorityFeePerGas } : undefined; + const txOptions = await getTxOptions(signer, chainId, maxFeePerGas, maxPriorityFeePerGas); + // Send the actual transaction if (isV5TT) { return await (titleEscrowContract as v5Contracts.TitleEscrow).transferHolder( @@ -177,16 +136,34 @@ const transferHolder = async ( } }; +/** + * Transfers the beneficiary role of a Title Escrow contract to a new beneficiary address. + * The caller of this function must be the current holder. + * @param {ContractOptions} contractOptions - Contains `tokenRegistryAddress` and optionally `tokenId` and `titleEscrowAddress`. + * @param {Signer | SignerV6} signer - The signer (ethers v5 or v6) who initiates and signs the transaction. + * @param {TransferBeneficiaryParams} params - Object containing: + * - `newBeneficiaryAddress`: Address to which the beneficiary role is being transferred. + * - `remarks` (optional): Optional encrypted message attached with the transaction. + * @param {TransactionOptions} options - Transaction configuration options: + * - `titleEscrowVersion` (optional): Token registry version, either `'v4'` or `'v5'`. + * - `chainId` (optional): Used to query gas station info if gas fee values are missing. + * - `maxFeePerGas`(optional), `maxPriorityFeePerGas`(optional): EIP-1559 gas fee parameters. + * - `id`(optional): Used for encryption of remarks. + * @throws If required values like `titleEscrowAddress` or `signer.provider` are missing. + * @throws If the version is unsupported (neither v4 nor v5). + * @throws If the dry-run `callStatic` fails for pre-checking the transaction. + * @returns {Promise} The transaction response for the `transferBeneficiary` call. + */ const transferBeneficiary = async ( contractOptions: ContractOptions, signer: Signer | SignerV6, params: TransferBeneficiaryParams, - options: TransferOptions, -) => { + options: TransactionOptions, +): Promise => { const { tokenId, tokenRegistryAddress } = contractOptions; const { titleEscrowVersion } = options; let { titleEscrowAddress } = contractOptions; - let { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; + const { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; let isV5TT = titleEscrowVersion === 'v5'; let isV4TT = titleEscrowVersion === 'v4'; @@ -265,19 +242,8 @@ const transferBeneficiary = async ( } // If gas values are missing, query gas station if available - if (!maxFeePerGas || !maxPriorityFeePerGas) { - chainId = chainId ?? ((await getChainIdSafe(signer)) as unknown as CHAIN_ID); - const gasStation = SUPPORTED_CHAINS[chainId]?.gasStation; - - if (gasStation) { - const gasFees = await gasStation(); - maxFeePerGas = gasFees?.maxFeePerGas ?? 0; - maxPriorityFeePerGas = gasFees?.maxPriorityFeePerGas ?? 0; - } - } + const txOptions = await getTxOptions(signer, chainId, maxFeePerGas, maxPriorityFeePerGas); - const txOptions = - maxFeePerGas && maxPriorityFeePerGas ? { maxFeePerGas, maxPriorityFeePerGas } : undefined; // Send the actual transaction if (isV5TT) { const tx = await (titleEscrowContract as v5Contracts.TitleEscrow).transferBeneficiary( @@ -294,16 +260,36 @@ const transferBeneficiary = async ( return tx; } }; + +/** + * Transfers both the holder and beneficiary roles of a Title Escrow contract to new addresses. + * The caller of this function must be the current holder and beneficiary both. + * @param {ContractOptions} contractOptions - Contains `tokenRegistryAddress` and optionally `tokenId` and `titleEscrowAddress`. + * @param {Signer | SignerV6} signer - The signer (ethers v5 or v6) who initiates and signs the transaction. + * @param {TransferOwnersParams} params - Object containing: + * - `newBeneficiaryAddress`: The new beneficiary address. + * - `newHolderAddress`: The new holder address. + * - `remarks` (optional): Optional remarks that will be encrypted and included with the transaction. + * @param {TransactionOptions} options - Transaction configuration options: + * - `titleEscrowVersion` (optional): Token registry version, either `'v4'` or `'v5'`. + * - `chainId` (optional): Used for gas station lookup if gas fee values are not provided. + * - `maxFeePerGas`(optional), `maxPriorityFeePerGas`(optional): EIP-1559 gas fee parameters. + * - `id`(optional): Used for encrypting remarks. + * @throws If required fields like `titleEscrowAddress` or `signer.provider` are missing. + * @throws If the title escrow version is unsupported. + * @throws If the pre-check `callStatic.transferOwners` fails. + * @returns {Promise} The transaction response from the `transferOwners` call. + */ const transferOwners = async ( contractOptions: ContractOptions, signer: Signer | SignerV6, params: TransferOwnersParams, - options: TransferOptions, -) => { + options: TransactionOptions, +): Promise => { const { tokenId, tokenRegistryAddress } = contractOptions; const { titleEscrowVersion } = options; let { titleEscrowAddress } = contractOptions; - let { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; + const { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; let isV5TT = titleEscrowVersion === 'v5'; let isV4TT = titleEscrowVersion === 'v4'; @@ -389,19 +375,8 @@ const transferOwners = async ( } // If gas values are missing, query gas station if available - if (!maxFeePerGas || !maxPriorityFeePerGas) { - chainId = chainId ?? ((await getChainIdSafe(signer)) as unknown as CHAIN_ID); - const gasStation = SUPPORTED_CHAINS[chainId]?.gasStation; - - if (gasStation) { - const gasFees = await gasStation(); - maxFeePerGas = gasFees?.maxFeePerGas ?? 0; - maxPriorityFeePerGas = gasFees?.maxPriorityFeePerGas ?? 0; - } - } + const txOptions = await getTxOptions(signer, chainId, maxFeePerGas, maxPriorityFeePerGas); - const txOptions = - maxFeePerGas && maxPriorityFeePerGas ? { maxFeePerGas, maxPriorityFeePerGas } : undefined; // Send the actual transaction if (isV5TT) { @@ -420,16 +395,34 @@ const transferOwners = async ( } }; +/** + * Nominates a new beneficiary on the Title Escrow contract. + * The caller of this function must be the current beneficiary. + * @param {ContractOptions} contractOptions - Contains `tokenRegistryAddress` and optionally `tokenId` and `titleEscrowAddress`. + * @param {Signer | SignerV6} signer - The signer (ethers v5 or v6) who will sign and send the transaction. + * @param {NominateParams} params - Nomination parameters: + * - `newBeneficiaryAddress`: The Ethereum address to nominate as the new beneficiary. + * - `remarks` (optional): Remarks to include with the transaction (will be encrypted). + * @param {TransactionOptions} options - Transaction-level configuration: + * - `titleEscrowVersion` (optional): Specifies token registry version, either `'v4'` or `'v5'`. + * - `chainId` (optional): Chain ID used for querying gas stations if fees are not set. + * - `maxFeePerGas`(optional), `maxPriorityFeePerGas`(optional): EIP-1559-compatible gas fee settings. + * - `id`(optional): Used for encrypting the remarks string. + * @throws If required inputs like `titleEscrowAddress` or `signer.provider` are missing. + * @throws If token registry version is unsupported. + * @throws If the dry-run `callStatic.nominate()` fails. + * @returns {Promise} The transaction response from the `nominate` method. + */ const nominate = async ( contractOptions: ContractOptions, signer: Signer | SignerV6, params: NominateParams, - options: TransferOptions, -) => { + options: TransactionOptions, +): Promise => { const { tokenId, tokenRegistryAddress } = contractOptions; const { titleEscrowVersion } = options; let { titleEscrowAddress } = contractOptions; - let { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; + const { chainId, maxFeePerGas, maxPriorityFeePerGas } = options; let isV5TT = titleEscrowVersion === 'v5'; let isV4TT = titleEscrowVersion === 'v4'; @@ -504,19 +497,8 @@ const nominate = async ( } // If gas values are missing, query gas station if available - if (!maxFeePerGas || !maxPriorityFeePerGas) { - chainId = chainId ?? ((await getChainIdSafe(signer)) as unknown as CHAIN_ID); - const gasStation = SUPPORTED_CHAINS[chainId]?.gasStation; - - if (gasStation) { - const gasFees = await gasStation(); - maxFeePerGas = gasFees?.maxFeePerGas ?? 0; - maxPriorityFeePerGas = gasFees?.maxPriorityFeePerGas ?? 0; - } - } + const txOptions = await getTxOptions(signer, chainId, maxFeePerGas, maxPriorityFeePerGas); - const txOptions = - maxFeePerGas && maxPriorityFeePerGas ? { maxFeePerGas, maxPriorityFeePerGas } : undefined; // Send the actual transaction if (isV5TT) { diff --git a/src/token-registry-functions/types.ts b/src/token-registry-functions/types.ts new file mode 100644 index 0000000..22b0951 --- /dev/null +++ b/src/token-registry-functions/types.ts @@ -0,0 +1,53 @@ +import { CHAIN_ID } from '@tradetrust-tt/tradetrust-utils'; +import { BigNumber } from 'ethers'; +import { BigNumberish } from 'ethersV6'; +import { providerV5, providerV6 } from 'src/__tests__/token-registry-functions/fixtures'; + +export type GasValue = BigNumber | BigNumberish | string | number; + +export interface RejectTransferParams { + remarks?: string; +} + +export interface TransactionOptions { + chainId?: CHAIN_ID; + titleEscrowVersion?: 'v4' | 'v5'; + maxFeePerGas?: BigNumberish | string | number | BigNumber; + maxPriorityFeePerGas?: BigNumberish | string | number | BigNumber; + id?: string; +} + +export type ContractOptions = + | { + titleEscrowAddress: string; // Present — no restrictions on the rest + tokenId?: string | number; + tokenRegistryAddress?: string; + } + | { + titleEscrowAddress?: undefined; // Absent — must provide both below + tokenId: string | number; + tokenRegistryAddress: string; + }; + +export interface TransferHolderParams { + holderAddress: string; + remarks?: string; +} +export interface TransferBeneficiaryParams { + newBeneficiaryAddress: string; + remarks?: string; +} +export interface NominateParams { + newBeneficiaryAddress: string; + remarks?: string; +} +export interface TransferOwnersParams { + newHolderAddress: string; + newBeneficiaryAddress: string; + remarks?: string; +} +export interface ProviderInfo { + Provider: typeof providerV5 | typeof providerV6; + ethersVersion: 'v5' | 'v6'; + titleEscrowVersion: 'v4' | 'v5'; +} diff --git a/src/token-registry-functions/utils.ts b/src/token-registry-functions/utils.ts new file mode 100644 index 0000000..d426481 --- /dev/null +++ b/src/token-registry-functions/utils.ts @@ -0,0 +1,44 @@ +import { isV6EthersProvider } from 'src/utils/ethers'; +import { GasValue } from './types'; +import { CHAIN_ID, SUPPORTED_CHAINS } from '@tradetrust-tt/tradetrust-utils'; +import { Signer } from 'ethers'; +import { Signer as SignerV6 } from 'ethersV6'; + +const getTxOptions = async ( + signer: SignerV6 | Signer, + chainId: CHAIN_ID, + maxFeePerGas: GasValue, + maxPriorityFeePerGas: GasValue, +) => { + // If gas values are missing, query gas station if available + if (!maxFeePerGas || !maxPriorityFeePerGas) { + chainId = chainId ?? ((await getChainIdSafe(signer)) as unknown as CHAIN_ID); + const gasStation = SUPPORTED_CHAINS[chainId]?.gasStation; + + if (gasStation) { + const gasFees = await gasStation(); + maxFeePerGas = gasFees?.maxFeePerGas ?? 0; + maxPriorityFeePerGas = gasFees?.maxPriorityFeePerGas ?? 0; + } + } + return maxFeePerGas && maxPriorityFeePerGas ? { maxFeePerGas, maxPriorityFeePerGas } : undefined; +}; + +// 🔍 Handles both Ethers v5 and v6 signer types +const getChainIdSafe = async (signer: SignerV6 | Signer): Promise => { + if (isV6EthersProvider(signer.provider)) { + const network = await (signer as Signer).provider?.getNetwork(); + if (!network?.chainId) throw new Error('Cannot determine chainId: provider is missing'); + return network.chainId; + } + return await (signer as Signer).getChainId(); +}; + +const getSignerAddressSafe = async (signer: SignerV6 | Signer): Promise => { + if (isV6EthersProvider(signer.provider)) { + return await (signer as SignerV6).getAddress(); + } + return await (signer as unknown as Signer).getAddress(); +}; + +export { getChainIdSafe, getTxOptions, getSignerAddressSafe };