From acd60d3ca59c46b9cef057c9b56021045d818f9d Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Wed, 5 Feb 2025 16:47:48 +0530 Subject: [PATCH 01/16] Add api to create a lock --- Anchor.toml | 2 + package-lock.json | 514 ++- package.json | 4 +- src/app/api/flux/route.ts | 1 + src/app/api/lock/route.ts | 172 + src/locker-utils/index.ts | 254 ++ .../token-2022/remaining-accounts.ts | 50 + .../token-2022/token-extensions.ts | 57 + src/utils/create-lock.ts | 95 + src/utils/uploadToPinata.ts | 43 +- target/idl/locker.json | 3143 ++++++++++++++++ target/types/locker.ts | 3149 +++++++++++++++++ 12 files changed, 7471 insertions(+), 13 deletions(-) create mode 100644 Anchor.toml create mode 100644 src/app/api/lock/route.ts create mode 100644 src/locker-utils/index.ts create mode 100644 src/locker-utils/token-2022/remaining-accounts.ts create mode 100644 src/locker-utils/token-2022/token-extensions.ts create mode 100644 src/utils/create-lock.ts create mode 100644 target/idl/locker.json create mode 100644 target/types/locker.ts diff --git a/Anchor.toml b/Anchor.toml new file mode 100644 index 0000000..48a5518 --- /dev/null +++ b/Anchor.toml @@ -0,0 +1,2 @@ +[provider] +cluster = "mainnet" diff --git a/package-lock.json b/package-lock.json index 81cedc9..b3adbb8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,11 @@ "name": "solana-meme-generator", "version": "0.1.0", "dependencies": { + "@coral-xyz/anchor": "^0.30.1", "@fal-ai/client": "^1.2.1", "@google/generative-ai": "^0.21.0", - "@solana/spl-token": "^0.3.8", + "@pinata/sdk": "^2.1.0", + "@solana/spl-token": "^0.3.11", "@solana/web3.js": "^1.78.4", "big.js": "^6.2.2", "bn.js": "^5.2.0", @@ -73,6 +75,64 @@ "@chainsafe/is-ip": "^2.0.1" } }, + "node_modules/@coral-xyz/anchor": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.30.1.tgz", + "integrity": "sha512-gDXFoF5oHgpriXAaLpxyWBHdCs8Awgf/gLHIo6crv7Aqm937CNdY+x+6hoj7QR5vaJV7MxWSQ0NGFzL3kPbWEQ==", + "dependencies": { + "@coral-xyz/anchor-errors": "^0.30.1", + "@coral-xyz/borsh": "^0.30.1", + "@noble/hashes": "^1.3.1", + "@solana/web3.js": "^1.68.0", + "bn.js": "^5.1.2", + "bs58": "^4.0.1", + "buffer-layout": "^1.2.2", + "camelcase": "^6.3.0", + "cross-fetch": "^3.1.5", + "crypto-hash": "^1.3.0", + "eventemitter3": "^4.0.7", + "pako": "^2.0.3", + "snake-case": "^3.0.4", + "superstruct": "^0.15.4", + "toml": "^3.0.0" + }, + "engines": { + "node": ">=11" + } + }, + "node_modules/@coral-xyz/anchor-errors": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/anchor-errors/-/anchor-errors-0.30.1.tgz", + "integrity": "sha512-9Mkradf5yS5xiLWrl9WrpjqOrAV+/W2RQHDlbnAZBivoGpOs1ECjoDCkVk4aRG8ZdiFiB8zQEVlxf+8fKkmSfQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@coral-xyz/anchor/node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, + "node_modules/@coral-xyz/anchor/node_modules/superstruct": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz", + "integrity": "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==" + }, + "node_modules/@coral-xyz/borsh": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.30.1.tgz", + "integrity": "sha512-aaxswpPrCFKl8vZTbxLssA2RvwX2zmKLlRCIktJOwW+VpVwYtXRtlWiIP+c2pPRKneiTiWCN2GEMSH9j1zTlWQ==", + "dependencies": { + "bn.js": "^5.1.2", + "buffer-layout": "^1.2.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@solana/web3.js": "^1.68.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -638,6 +698,53 @@ "node": ">=10" } }, + "node_modules/@pinata/sdk": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@pinata/sdk/-/sdk-2.1.0.tgz", + "integrity": "sha512-hkS0tcKtsjf9xhsEBs2Nbey5s+Db7x5rlOH9TaWHBXkJ7IwwOs2xnEDigNaxAHKjYAwcw+m2hzpO5QgOfeF7Zw==", + "deprecated": "Please install the new IPFS SDK at pinata-web3. More information at https://docs.pinata.cloud/web3/sdk", + "dependencies": { + "axios": "^0.21.1", + "form-data": "^2.3.3", + "is-ipfs": "^0.6.0", + "path": "^0.12.7" + } + }, + "node_modules/@pinata/sdk/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/@pinata/sdk/node_modules/form-data": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", + "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12", + "safe-buffer": "^5.2.1" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/@pinata/sdk/node_modules/is-ipfs": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/is-ipfs/-/is-ipfs-0.6.3.tgz", + "integrity": "sha512-HyRot1dvLcxImtDqPxAaY1miO6WsiP/z7Yxpg2qpaLWv5UdhAPtLvHJ4kMLM0w8GSl8AFsVF23PHe1LzuWrUlQ==", + "dependencies": { + "bs58": "^4.0.1", + "cids": "~0.7.0", + "mafmt": "^7.0.0", + "multiaddr": "^7.2.1", + "multibase": "~0.6.0", + "multihashes": "~0.4.13" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -1763,6 +1870,14 @@ "ieee754": "^1.2.1" } }, + "node_modules/buffer-layout": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz", + "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==", + "engines": { + "node": ">=4.5" + } + }, "node_modules/bufferutil": { "version": "4.0.9", "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz", @@ -1872,6 +1987,17 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -1955,6 +2081,51 @@ "node": ">=10" } }, + "node_modules/cids": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", + "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.5.0", + "class-is": "^1.1.0", + "multibase": "~0.6.0", + "multicodec": "^1.0.0", + "multihashes": "~0.4.15" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, + "node_modules/cids/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/class-is": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", + "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" + }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -2125,6 +2296,14 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "devOptional": true }, + "node_modules/cross-fetch": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", + "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2138,6 +2317,17 @@ "node": ">= 8" } }, + "node_modules/crypto-hash": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz", + "integrity": "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -2393,6 +2583,15 @@ "node": ">=6.0.0" } }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dotenv": { "version": "16.4.7", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", @@ -3992,6 +4191,14 @@ "node": ">= 12" } }, + "node_modules/ip-regex": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", + "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -4202,6 +4409,17 @@ "node": ">=0.10.0" } }, + "node_modules/is-ip": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", + "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", + "dependencies": { + "ip-regex": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-ipfs": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/is-ipfs/-/is-ipfs-8.0.4.tgz", @@ -4714,6 +4932,14 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4726,6 +4952,14 @@ "node": ">=10" } }, + "node_modules/mafmt": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/mafmt/-/mafmt-7.1.0.tgz", + "integrity": "sha512-vpeo9S+hepT3k2h5iFxzEHvvR0GPBx9uKaErmnRzYNcaKb03DgOArjEMlgG4a9LcuZZ89a3I8xbeto487n26eA==", + "dependencies": { + "multiaddr": "^7.3.0" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -4949,11 +5183,226 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/multiaddr": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/multiaddr/-/multiaddr-7.5.0.tgz", + "integrity": "sha512-GvhHsIGDULh06jyb6ev+VfREH9evJCFIRnh3jUt9iEZ6XDbyoisZRFEI9bMvK/AiR6y66y6P+eoBw9mBYMhMvw==", + "deprecated": "This module is deprecated, please upgrade to @multiformats/multiaddr", + "dependencies": { + "buffer": "^5.5.0", + "cids": "~0.8.0", + "class-is": "^1.1.0", + "is-ip": "^3.1.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + } + }, + "node_modules/multiaddr/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/multiaddr/node_modules/cids": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/cids/-/cids-0.8.3.tgz", + "integrity": "sha512-yoXTbV3llpm+EBGWKeL9xKtksPE/s6DPoDSY4fn8I8TEW1zehWXPSB0pwAXVDlLaOlrw+sNynj995uD9abmPhA==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.6.0", + "class-is": "^1.1.0", + "multibase": "^1.0.0", + "multicodec": "^1.0.1", + "multihashes": "^1.0.1" + }, + "engines": { + "node": ">=4.0.0", + "npm": ">=3.0.0" + } + }, + "node_modules/multiaddr/node_modules/cids/node_modules/multibase": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-1.0.1.tgz", + "integrity": "sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + }, + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multiaddr/node_modules/multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/multiaddr/node_modules/multihashes": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-1.0.1.tgz", + "integrity": "sha512-S27Tepg4i8atNiFaU5ZOm3+gl3KQlUanLs/jWcBxQHFttgq+5x1OgbQmf2d8axJ/48zYGBd/wT9d723USMFduw==", + "dependencies": { + "buffer": "^5.6.0", + "multibase": "^1.0.1", + "varint": "^5.0.0" + }, + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multiaddr/node_modules/multihashes/node_modules/multibase": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-1.0.1.tgz", + "integrity": "sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + }, + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + } + }, + "node_modules/multibase": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", + "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, + "node_modules/multibase/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/multicodec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", + "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "buffer": "^5.6.0", + "varint": "^5.0.0" + } + }, + "node_modules/multicodec/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/multiformats": { "version": "13.3.1", "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.3.1.tgz", "integrity": "sha512-QxowxTNwJ3r5RMctoGA5p13w5RbRT2QDkoM+yFlqfLiioBp78nhDjnRLvmSBI9+KAqN4VdgOVWM9c0CHd86m3g==" }, + "node_modules/multihashes": { + "version": "0.4.21", + "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", + "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", + "dependencies": { + "buffer": "^5.5.0", + "multibase": "^0.7.0", + "varint": "^5.0.0" + } + }, + "node_modules/multihashes/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/multihashes/node_modules/multibase": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", + "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", + "deprecated": "This module has been superseded by the multiformats module", + "dependencies": { + "base-x": "^3.0.8", + "buffer": "^5.5.0" + } + }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -5073,6 +5522,15 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, "node_modules/node-abi": { "version": "3.74.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.74.0.tgz", @@ -5498,6 +5956,11 @@ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==" }, + "node_modules/pako": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", + "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5528,6 +5991,15 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5864,6 +6336,14 @@ "node": ">= 0.8.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/progress-events": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/progress-events/-/progress-events-1.0.1.tgz", @@ -6551,6 +7031,15 @@ "npm": ">= 3.0.0" } }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/socks": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", @@ -7112,6 +7601,11 @@ "node": ">=8.0" } }, + "node_modules/toml": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz", + "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==" + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -7646,11 +8140,24 @@ "node": ">=6.14.2" } }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/util/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -7665,6 +8172,11 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "devOptional": true }, + "node_modules/varint": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", + "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index 077e5bc..99aebb9 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,11 @@ "lint": "next lint" }, "dependencies": { + "@coral-xyz/anchor": "^0.30.1", "@fal-ai/client": "^1.2.1", "@google/generative-ai": "^0.21.0", - "@solana/spl-token": "^0.3.8", + "@pinata/sdk": "^2.1.0", + "@solana/spl-token": "^0.3.11", "@solana/web3.js": "^1.78.4", "big.js": "^6.2.2", "bn.js": "^5.2.0", diff --git a/src/app/api/flux/route.ts b/src/app/api/flux/route.ts index afd9193..95a7082 100644 --- a/src/app/api/flux/route.ts +++ b/src/app/api/flux/route.ts @@ -88,6 +88,7 @@ export async function POST(req: NextRequest): Promise { // Extract the image URL from the response const imageUrl = result.data?.images?.[0]?.url + console.log(imageUrl); if (!imageUrl) { console.error('No image URL in response:', result) diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts new file mode 100644 index 0000000..422e0ae --- /dev/null +++ b/src/app/api/lock/route.ts @@ -0,0 +1,172 @@ +import { NextRequest, NextResponse } from "next/server"; +import { Connection, Keypair, ParsedInstruction, ParsedTransactionWithMeta, PartiallyDecodedInstruction, PublicKey } from "@solana/web3.js"; +import { createLock } from "../../../utils/create-lock"; +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import assert from "assert"; +import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; +import { BN, min } from "bn.js"; + +assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); + +const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); +const MTM_MINT_ADDRESS = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT; + +function isParsedInstruction( + instruction: ParsedInstruction | PartiallyDecodedInstruction + ): instruction is ParsedInstruction { + return (instruction as ParsedInstruction).parsed !== undefined; + } + +async function extractMTMTransferDetails( +connection: Connection, +signature: string, +mtmMintAddress: string // Mint address of the MTM token +) { +try { + // Fetch the transaction details using the signature + const transaction: ParsedTransactionWithMeta | null = await connection.getParsedTransaction(signature, { + maxSupportedTransactionVersion: 0, // Ensure compatibility with legacy transactions + }); + + if (!transaction) { + throw new Error('Transaction not found'); + } + + // Extract the "from", "to", and "amount" for the MTM token transfer + let fromAddress: string | null = null; + let toAddress: string | null = null; + let tokenAmount: number | null = null; + + // Function to process instructions + const processInstructions = (instructions: ParsedInstruction[]) => { + instructions.forEach((instruction: ParsedInstruction) => { + if (instruction.programId.equals(new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'))) { + // Check if the instruction is a transfer instruction + if (instruction.parsed.type === 'transfer' || instruction.parsed.type === 'transferChecked') { + const parsedInfo = instruction.parsed.info; + const mint = parsedInfo.mint; + + // Check if the mint address matches the provided MTM mint address + if (mint === mtmMintAddress) { + fromAddress = parsedInfo.source; + toAddress = parsedInfo.destination; + tokenAmount = parsedInfo.amount; + } + } + } + }); + }; + + // Filter out PartiallyDecodedInstruction and process only ParsedInstruction + const parsedInstructions = transaction.transaction.message.instructions.filter(isParsedInstruction); + + // Process top-level instructions + processInstructions(parsedInstructions); + + // Process inner instructions (if any) + if (transaction.meta?.innerInstructions) { + transaction.meta.innerInstructions.forEach((inner) => { + const innerParsedInstructions = inner.instructions.filter(isParsedInstruction); + processInstructions(innerParsedInstructions); + }); + } + + if (!fromAddress || !toAddress || !tokenAmount) { + throw new Error('No matching MTM token transfer found in the transaction'); + } + + return { fromAddress, toAddress, tokenAmount }; +} catch (error) { + console.error('Error extracting MTM transfer details:', error); + throw error; +} +} + +export async function GET(req: NextRequest) { + try { + // Get signature from URL params + const { searchParams } = new URL(req.url); + const signature = searchParams.get('signature') || '4HBtnoNUuMGpmbhD9cPiJtbxkhux31pfZs3HYud5eopAU69RaC4UbJsYdj83eafFxV6eH8pSaRgqELrwyjrWp7yz'; + let amount = new BN(0); + + if (!signature) { + return NextResponse.json( + { error: "Transaction signature is required" }, + { status: 400 } + ); + } + + if (!MTM_MINT_ADDRESS) { + return NextResponse.json( + { error: "MTM_MINT_ADDRESS environment variable is not set" }, + { status: 500 } + ); + } + + // Extract transaction details + // const result = await extractMTMTransferDetails( + // connection, + // signature, + // MTM_MINT_ADDRESS + // ); + + // if (!result) { + // return NextResponse.json( + // { error: "Failed to extract transaction details" }, + // { status: 500 } + // ); + // } + + // console.log({ result }); + + // // Validate extracted values + // if (!result.fromAddress || result.tokenAmount <= 0) { + // return NextResponse.json( + // { error: "Invalid transaction details extracted" }, + // { status: 400 } + // ); + // } + + assert(process.env.USER_PRIVATE_KEY, 'USER_PRIVATE_KEY is required'); + assert(process.env.CLIFF_TIME, 'CLIFF_TIME is required'); + + const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY; + const CLIFF_TIME = process.env.CLIFF_TIME; + + const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); + const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); + const recipientPublicKey = new PublicKey('Bnnq8n3rRKZe8NJAYn4vBkxp1v8Bnc6zXpUPpDeujCu'); + + amount = amount.add(new BN(1000000)); + + // Call createLock function with extracted values + let escrow; + try { + escrow = await createLock(tokenLockerKeypair, recipientPublicKey, duration, amount); + } catch (error) { + console.error('Error creating lock:', error); + return NextResponse.json( + { error: "Failed to create lock" }, + { status: 500 } + ); + } + + console.log({ escrow }); + + // Return successful response + return NextResponse.json({ + success: true, + data: { + sender: recipientPublicKey, + amount: amount, + } + }); + + } catch (error) { + console.error('API route error:', error); + return NextResponse.json( + { error: "Internal server error" }, + { status: 500 } + ); + } +} diff --git a/src/locker-utils/index.ts b/src/locker-utils/index.ts new file mode 100644 index 0000000..d732746 --- /dev/null +++ b/src/locker-utils/index.ts @@ -0,0 +1,254 @@ +/** + * Methods from jup-lock: + * - createLockerProgram + * - deriveEscrow + * - createVestingPlanV2 + * Reference: https://github.com/jup-ag/jup-lock/blob/main/tests/locker_utils/index.ts + */ + +import assert from 'assert'; +import 'dotenv/config'; + +import { + ASSOCIATED_TOKEN_PROGRAM_ID, + createAssociatedTokenAccountInstruction, + getAssociatedTokenAddressSync, + TOKEN_2022_PROGRAM_ID, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token'; +import { + AnchorProvider, + BN, + Program, + Wallet, + web3, + workspace, +} from '@coral-xyz/anchor'; +import { AccountMeta, Connection, SendTransactionError } from '@solana/web3.js'; + +// TODO: Generate type file from IDL json +import { Locker } from '../../target/types/locker'; +import { TokenExtensionUtil } from './token-2022/token-extensions'; +import { + OptionRemainingAccountsInfoData, + RemainingAccountsBuilder, + RemainingAccountsType, +} from './token-2022/remaining-accounts'; + +assert(process.env.RPC_ENDPOINT); + +const connection = new Connection(process.env.RPC_ENDPOINT); + +const ESCROW_USE_SPL_TOKEN = 0; + +const MEMO_PROGRAM = new web3.PublicKey( + "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" +); + +export function createLockerProgram(wallet: Wallet): Program { + const provider = new AnchorProvider(connection, wallet, { + maxRetries: 3, + }); + provider.opts.commitment = 'confirmed'; + + return workspace.Locker as Program; +} + +export function deriveEscrow(base: web3.PublicKey, programId: web3.PublicKey) { + return web3.PublicKey.findProgramAddressSync( + [Buffer.from('escrow'), base.toBuffer()], + programId + ); +} + +export interface CreateVestingPlanParams { + ownerKeypair: web3.Keypair; + tokenMint: web3.PublicKey; + isAssertion: boolean; + vestingStartTime: BN; + cliffTime: BN; + frequency: BN; + cliffUnlockAmount: BN; + amountPerPeriod: BN; + numberOfPeriod: BN; + recipient: web3.PublicKey; + updateRecipientMode: number; + cancelMode: number; + tokenProgram?: web3.PublicKey; +} + +// V2 instructions +export async function createVestingPlanV2(params: CreateVestingPlanParams) { + let { + tokenMint, + ownerKeypair, + vestingStartTime, + cliffTime, + frequency, + cliffUnlockAmount, + amountPerPeriod, + numberOfPeriod, + recipient, + updateRecipientMode, + cancelMode, + tokenProgram, + } = params; + + const program = createLockerProgram(new Wallet(ownerKeypair)); + + const baseKP = web3.Keypair.generate(); + + let [escrow] = deriveEscrow(baseKP.publicKey, program.programId); + + const senderToken = getAssociatedTokenAddressSync( + tokenMint, + ownerKeypair.publicKey, + false, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + const escrowToken = getAssociatedTokenAddressSync( + tokenMint, + escrow, + true, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + let remainingAccountsInfo = null; + let remainingAccounts: AccountMeta[] = []; + if (tokenProgram == TOKEN_2022_PROGRAM_ID) { + let inputTransferHookAccounts = + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + program.provider.connection, + tokenMint, + senderToken, + escrowToken, + ownerKeypair.publicKey, + TOKEN_2022_PROGRAM_ID + ); + + [remainingAccountsInfo, remainingAccounts] = new RemainingAccountsBuilder() + .addSlice( + RemainingAccountsType.TransferHookEscrow, + inputTransferHookAccounts + ) + .build() as [OptionRemainingAccountsInfoData, AccountMeta[]]; + } + + assert(tokenProgram); + + try { + const transaction = await program.methods + .createVestingEscrowV2( + { + vestingStartTime, + cliffTime, + frequency, + cliffUnlockAmount, + amountPerPeriod, + numberOfPeriod, + updateRecipientMode, + cancelMode, + }, + remainingAccountsInfo + ) + .accounts({ + base: baseKP.publicKey, + senderToken, + escrowToken, + recipient, + tokenMint, + sender: ownerKeypair.publicKey, + tokenProgram, + systemProgram: web3.SystemProgram.programId, + escrow, + // TODO: Fix type error for escrowToken + } as any) + .remainingAccounts(remainingAccounts ? remainingAccounts : []) + .preInstructions([ + createAssociatedTokenAccountInstruction( + ownerKeypair.publicKey, + escrowToken, + escrow, + tokenMint, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ), + ]) + .signers([baseKP, ownerKeypair]) + .rpc(); + + return escrow; + } catch (error) { + if (error instanceof SendTransactionError) { + console.error('Transaction failed:', error.message); + console.error('Logs:', error.logs); + } + throw error; + } +} + +export interface ClaimTokenParamsV2 { + isAssertion: boolean; + escrow: web3.PublicKey; + recipient: web3.Keypair; + maxAmount: BN; + recipientToken: web3.PublicKey; + tokenProgram: web3.PublicKey; +} + +export async function claimTokenV2(params: ClaimTokenParamsV2) { + let { isAssertion, escrow, recipient, maxAmount, recipientToken } = params; + const program = createLockerProgram(new Wallet(recipient)); + const escrowState = await program.account.vestingEscrow.fetch(escrow); + const tokenProgram = + escrowState.tokenProgramFlag == ESCROW_USE_SPL_TOKEN + ? TOKEN_PROGRAM_ID + : TOKEN_2022_PROGRAM_ID; + + const escrowToken = getAssociatedTokenAddressSync( + escrowState.tokenMint, + escrow, + true, + tokenProgram, + ASSOCIATED_TOKEN_PROGRAM_ID + ); + + let remainingAccountsInfo = null; + let remainingAccounts: AccountMeta[] | undefined = []; + if (tokenProgram == TOKEN_2022_PROGRAM_ID) { + let claimTransferHookAccounts = + await TokenExtensionUtil.getExtraAccountMetasForTransferHook( + program.provider.connection, + escrowState.tokenMint, + escrowToken, + recipientToken, + escrow, + TOKEN_2022_PROGRAM_ID + ); + + [remainingAccountsInfo, remainingAccounts] = new RemainingAccountsBuilder() + .addSlice( + RemainingAccountsType.TransferHookEscrow, + claimTransferHookAccounts + ) + .build(); + } + + const tx = await program.methods + .claimV2(maxAmount, remainingAccountsInfo) + .accounts({ + tokenProgram, + tokenMint: escrowState.tokenMint, + memoProgram: MEMO_PROGRAM, + escrow, + escrowToken, + recipient: recipient.publicKey, + recipientToken, + } as any) + .remainingAccounts(remainingAccounts ? remainingAccounts : []) + .signers([recipient]) + .rpc(); +} diff --git a/src/locker-utils/token-2022/remaining-accounts.ts b/src/locker-utils/token-2022/remaining-accounts.ts new file mode 100644 index 0000000..e30c082 --- /dev/null +++ b/src/locker-utils/token-2022/remaining-accounts.ts @@ -0,0 +1,50 @@ +// Reference: https://github.com/jup-ag/jup-lock/blob/main/tests/locker_utils/index.ts + +import { AccountMeta } from '@solana/web3.js'; + +export enum RemainingAccountsType { + TransferHookEscrow = 'transferHookEscrow', +} + +type RemainingAccountsAnchorType = { transferHookEscrow: {} }; + +export type RemainingAccountsSliceData = { + accountsType: RemainingAccountsAnchorType; + length: number; +}; + +export type RemainingAccountsInfoData = { + slices: RemainingAccountsSliceData[]; +}; + +// Option on Rust +// null is treated as None in Rust. undefined doesn't work. +export type OptionRemainingAccountsInfoData = RemainingAccountsInfoData | null; + +export class RemainingAccountsBuilder { + private remainingAccounts: AccountMeta[] = []; + private slices: RemainingAccountsSliceData[] = []; + + constructor() {} + + addSlice( + accountsType: RemainingAccountsType, + accounts?: AccountMeta[] + ): this { + if (!accounts || accounts.length === 0) return this; + + this.slices.push({ + accountsType: { [accountsType]: {} } as RemainingAccountsAnchorType, + length: accounts.length, + }); + this.remainingAccounts.push(...accounts); + + return this; + } + + build(): [OptionRemainingAccountsInfoData, AccountMeta[] | undefined] { + return this.slices.length === 0 + ? [null, undefined] + : [{ slices: this.slices }, this.remainingAccounts]; + } +} diff --git a/src/locker-utils/token-2022/token-extensions.ts b/src/locker-utils/token-2022/token-extensions.ts new file mode 100644 index 0000000..7a779ef --- /dev/null +++ b/src/locker-utils/token-2022/token-extensions.ts @@ -0,0 +1,57 @@ +// Reference: https://github.com/jup-ag/jup-lock/blob/main/tests/locker_utils/index.ts + +import { + AccountMeta, + Connection, + PublicKey, + TransactionInstruction, +} from '@solana/web3.js'; +import { + addExtraAccountsToInstruction, + getMint, + getTransferHook, + TOKEN_2022_PROGRAM_ID, +} from '@solana/spl-token'; + +export class TokenExtensionUtil { + public static async getExtraAccountMetasForTransferHook( + connection: Connection, + tokenMint: PublicKey, + source: PublicKey, + destination: PublicKey, + owner: PublicKey, + tokenProgram: PublicKey + ): Promise { + let mint = await getMint(connection, tokenMint, 'confirmed', tokenProgram); + const transferHook = getTransferHook(mint); + + if (!transferHook) return undefined; + + const instruction = new TransactionInstruction({ + programId: TOKEN_2022_PROGRAM_ID, + keys: [ + { pubkey: source, isSigner: false, isWritable: false }, + { + pubkey: tokenMint, + isSigner: false, + isWritable: false, + }, + { pubkey: destination, isSigner: false, isWritable: false }, + { pubkey: owner, isSigner: false, isWritable: false }, + { pubkey: owner, isSigner: false, isWritable: false }, + ], + }); + + // Note: + await addExtraAccountsToInstruction( + connection, + instruction, + tokenMint, + 'confirmed', + transferHook.programId, + ); + + const extraAccountMetas = instruction.keys.slice(5); + return extraAccountMetas.length > 0 ? extraAccountMetas : undefined; + } +} diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts new file mode 100644 index 0000000..8b540f6 --- /dev/null +++ b/src/utils/create-lock.ts @@ -0,0 +1,95 @@ +import assert from 'assert'; +import BN from 'bn.js'; +import 'dotenv/config'; +import bs58 from 'bs58'; + +import * as anchor from "@coral-xyz/anchor"; +import { + TOKEN_PROGRAM_ID, +} from "@solana/spl-token"; +import { Connection, PublicKey } from "@solana/web3.js"; + +import { createVestingPlanV2 } from '../locker-utils'; + +assert(process.env.RPC_ENDPOINT); +assert(process.env.USER_PRIVATE_KEY); +//assert(process.env.RECIPIENT_PUBLIC_KEY); +assert(process.env.MTM_MINT); + +const RPC_ENDPOINT= process.env.RPC_ENDPOINT; +const MTM_MINT = process.env.MTM_MINT; +const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY; + +const userKP = anchor.web3.Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); + +const connection = new Connection(RPC_ENDPOINT); +const token = new PublicKey(MTM_MINT); + +const provider = new anchor.AnchorProvider( + connection, + new anchor.Wallet(userKP), + // Commitment level required for simulating transaction + { preflightCommitment: 'processed' } +); + +anchor.setProvider(provider); + +export async function getMTMBalance (senderKeypair: anchor.web3.Keypair): Promise { + const mintPublicKey = new PublicKey(MTM_MINT); + const publicKey = senderKeypair.publicKey; + const tokenAccounts = await connection.getTokenAccountsByOwner(publicKey, { mint: mintPublicKey }); + + let balance = new BN(0); + for (const tokenAccount of tokenAccounts.value) { + const accountInfo = await connection.getParsedAccountInfo( + tokenAccount.pubkey, + "confirmed" + ); + + if (!accountInfo.value || Buffer.isBuffer(accountInfo.value.data)) { + console.warn( + `Token account ${tokenAccount.pubkey.toBase58()} data is not parsed.` + ); + continue; + } + + const tokenAmount = + (accountInfo.value.data as any).parsed?.info?.tokenAmount?.amount || "0"; + balance = balance.add(new BN(tokenAmount)); + } + + return balance; +}; + +export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipientPubKey: anchor.web3.PublicKey, duration: BN, balance: BN): Promise { + //const balance = await getMTMBalance(tokenLockerKeypair); + + if (balance.eq(new BN(0))) { + console.log('No balance available to create lock, skipping...'); + return; + } + + console.log('Creating a lock...'); + + const escrow = await createVestingPlanV2({ + ownerKeypair: tokenLockerKeypair, + vestingStartTime: new BN(Math.floor(Date.now() / 1000) - 60), // Start immediately + tokenMint: token, + isAssertion: true, + cliffTime: duration, + frequency: new BN(1), // Not needed since full unlock happens at cliff + cliffUnlockAmount: balance, // The entire amount should be released at cliff + amountPerPeriod: new BN(0), // No tokens should be released before cliff + numberOfPeriod: new BN(1), // Only release tokens once + recipient: recipientPubKey, + updateRecipientMode: 0, + cancelMode: 1, + tokenProgram: TOKEN_PROGRAM_ID, + }); + + if (escrow) { + console.log('Lock created successfully: ', escrow.toString()); + } + + return escrow; +} diff --git a/src/utils/uploadToPinata.ts b/src/utils/uploadToPinata.ts index 92c6a09..3061d58 100644 --- a/src/utils/uploadToPinata.ts +++ b/src/utils/uploadToPinata.ts @@ -1,28 +1,49 @@ -import { PinataSDK } from 'pinata-web3'; +import PinataClient from "@pinata/sdk" import 'dotenv/config'; import assert from 'assert'; - import { FluxGenerationResult } from '../services/fluxService'; +import axios from 'axios'; +import FormData from 'form-data'; -assert(process.env.PINATA_JWT, "PINATA_JWT is required"); -assert(process.env.PINATA_GATEWAY, "PINATA_GATEWAY is required"); +assert(process.env.PINATA_API_KEY, "PINATA_API_KEY is required"); +assert(process.env.PINATA_SECRET_KEY, "PINATA_SECRET_KEY is required"); +assert(process.env.PINATA_JWT, "PINATA_SECRET_KEY is required"); +assert(process.env.PINATA_GATEWAY); -const pinata = new PinataSDK({ - pinataJwt: process.env.PINATA_JWT, - pinataGateway: process.env.PINATA_GATEWAY, +const pinata = new PinataClient({ + pinataApiKey: process.env.PINATA_API_KEY, + pinataSecretApiKey: process.env.PINATA_SECRET_KEY, + pinataJWTKey: process.env.PINATA_JWT }); + export async function uploadToPinata(imageUrl: string): Promise { try { - const upload = await pinata.upload.url(imageUrl); + const response = await axios.get(imageUrl, { + responseType: 'arraybuffer' + }); - const publicURL = await pinata.gateways.convert(upload.IpfsHash); + // Create form data + const formData = new FormData(); + formData.append('file', Buffer.from(response.data), { + filename: `image-${Date.now()}.jpg`, + contentType: 'image/jpeg', + }); + + // Use the correct Pinata method for file upload + const result = await pinata.pinFileToIPFS(formData); + + if (!result.IpfsHash) { + throw new Error('IPFS hash not received from Pinata'); + } + + const publicURL = `${process.env.PINATA_GATEWAY}/ipfs/${result.IpfsHash}`; return { imageUrl: publicURL }; } catch (error) { - console.error('Error uploading to Pinata:', error) + console.error('Error uploading to Pinata:', error); return { error: error instanceof Error ? error.message : 'Upload failed' }; } -} +} \ No newline at end of file diff --git a/target/idl/locker.json b/target/idl/locker.json new file mode 100644 index 0000000..89f109a --- /dev/null +++ b/target/idl/locker.json @@ -0,0 +1,3143 @@ +{ + "address": "LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn", + "metadata": { + "name": "locker", + "version": "0.4.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ + { + "name": "cancel_vesting_escrow", + "docs": [ + "Cancel a vesting escrow.", + "- The claimable token will be transferred to recipient", + "- The remaining token will be transferred to the creator", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 217, + 233, + 13, + 3, + 143, + 101, + 53, + 201 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "token_mint", + "docs": [ + "Mint." + ], + "writable": true, + "relations": [ + "escrow" + ] + }, + { + "name": "escrow_token", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "creator_token", + "docs": [ + "Creator Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "recipient_token", + "docs": [ + "Receipient Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "rent_receiver", + "docs": [ + "CHECKED: The Token Account will receive the rent" + ], + "writable": true + }, + { + "name": "signer", + "docs": [ + "Signer." + ], + "writable": true, + "signer": true + }, + { + "name": "memo_program", + "docs": [ + "Memo program." + ], + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "token_program", + "docs": [ + "Token program." + ] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "remaining_accounts_info", + "type": { + "option": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "claim", + "docs": [ + "Claim maximum amount from the vesting escrow", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* max_amount - The maximum amount claimed by the recipient", + "" + ], + "discriminator": [ + 62, + 198, + 214, + 193, + 213, + 159, + 108, + 210 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "escrow_token", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "escrow" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "recipient", + "docs": [ + "Recipient." + ], + "writable": true, + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "recipient_token", + "docs": [ + "Recipient Token Account." + ], + "writable": true + }, + { + "name": "token_program", + "docs": [ + "Token program." + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "max_amount", + "type": "u64" + } + ] + }, + { + "name": "claim_v2", + "docs": [ + "Claim maximum amount from the vesting escrow", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* max_amount - The maximum amount claimed by the recipient", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 229, + 87, + 46, + 162, + 21, + 157, + 231, + 114 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "token_mint", + "docs": [ + "Mint." + ], + "relations": [ + "escrow" + ] + }, + { + "name": "escrow_token", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "recipient", + "docs": [ + "Recipient." + ], + "writable": true, + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "recipient_token", + "docs": [ + "Recipient Token Account." + ], + "writable": true + }, + { + "name": "memo_program", + "docs": [ + "Memo program." + ], + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "token_program", + "docs": [ + "Token program." + ] + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "max_amount", + "type": "u64" + }, + { + "name": "remaining_accounts_info", + "type": { + "option": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "close_vesting_escrow", + "docs": [ + "Close vesting escrow", + "- Close vesting escrow and escrow ATA and escrow metadata if recipient already claimed all tokens", + "- Rent receiver must be escrow's creator", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 221, + 185, + 95, + 135, + 136, + 67, + 252, + 87 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "escrow_metadata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119, + 95, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "escrow" + } + ] + } + }, + { + "name": "token_mint", + "docs": [ + "Mint." + ], + "writable": true + }, + { + "name": "escrow_token", + "writable": true + }, + { + "name": "creator_token", + "writable": true + }, + { + "name": "creator", + "docs": [ + "Creator." + ], + "writable": true, + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "token_program", + "docs": [ + "Token program." + ] + }, + { + "name": "memo_program", + "docs": [ + "Memo program." + ], + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "remaining_accounts_info", + "type": { + "option": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "create_root_escrow", + "docs": [ + "Create root escrow" + ], + "discriminator": [ + 116, + 212, + 12, + 188, + 77, + 226, + 32, + 201 + ], + "accounts": [ + { + "name": "base", + "signer": true + }, + { + "name": "root_escrow", + "docs": [ + "Root Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 114, + 111, + 111, + 116, + 95, + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + }, + { + "kind": "account", + "path": "token_mint" + }, + { + "kind": "arg", + "path": "params.version" + } + ] + } + }, + { + "name": "token_mint" + }, + { + "name": "payer", + "docs": [ + "payer." + ], + "writable": true, + "signer": true + }, + { + "name": "creator" + }, + { + "name": "system_program", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "CreateRootEscrowParameters" + } + } + } + ] + }, + { + "name": "create_vesting_escrow", + "docs": [ + "Create a vesting escrow for the given params", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* params - The params needed by instruction.", + "* vesting_start_time - The creation time of this escrow", + "* cliff_time - Trade cliff time of the escrow", + "* frequency - How frequent the claimable amount will be updated", + "* cliff_unlock_amount - The amount unlocked after cliff time", + "* amount_per_period - The amount unlocked per vesting period", + "* number_of_period - The total number of vesting period", + "* update_recipient_mode - Decide who can update the recipient of the escrow", + "* cancel_mode - Decide who can cancel the the escrow", + "" + ], + "discriminator": [ + 23, + 100, + 197, + 94, + 222, + 153, + 38, + 90 + ], + "accounts": [ + { + "name": "base", + "docs": [ + "Base." + ], + "writable": true, + "signer": true + }, + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + } + ] + } + }, + { + "name": "escrow_token", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "sender_token.mint", + "account": "TokenAccount" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "sender", + "docs": [ + "Sender." + ], + "writable": true, + "signer": true + }, + { + "name": "sender_token", + "docs": [ + "Sender Token Account." + ], + "writable": true + }, + { + "name": "recipient" + }, + { + "name": "token_program", + "docs": [ + "Token program." + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "system_program", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "CreateVestingEscrowParameters" + } + } + } + ] + }, + { + "name": "create_vesting_escrow_from_root", + "docs": [ + "Crate vesting escrow from root" + ], + "discriminator": [ + 6, + 238, + 161, + 108, + 252, + 114, + 246, + 91 + ], + "accounts": [ + { + "name": "root_escrow", + "docs": [ + "Root Escrow." + ], + "writable": true + }, + { + "name": "base", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 97, + 115, + 101 + ] + }, + { + "kind": "account", + "path": "root_escrow" + }, + { + "kind": "account", + "path": "recipient" + } + ] + } + }, + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + } + ] + } + }, + { + "name": "escrow_token", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "root_escrow_token", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "root_escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "token_mint", + "docs": [ + "Mint." + ], + "relations": [ + "root_escrow" + ] + }, + { + "name": "payer", + "docs": [ + "Rent Payer" + ], + "writable": true, + "signer": true + }, + { + "name": "recipient" + }, + { + "name": "system_program", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "token_program", + "docs": [ + "Token program." + ] + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "CreateVestingEscrowFromRootParams" + } + } + }, + { + "name": "proof", + "type": { + "vec": { + "array": [ + "u8", + 32 + ] + } + } + }, + { + "name": "remaining_accounts_info", + "type": { + "option": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "create_vesting_escrow_metadata", + "docs": [ + "Create vesting escrow metadata", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* params - The params needed by instruction.", + "* name - The name of the vesting escrow", + "* description - The description of the vesting escrow", + "* creator_email - The email of the creator", + "* recipient_email - The email of the recipient", + "" + ], + "discriminator": [ + 93, + 78, + 33, + 103, + 173, + 125, + 70, + 0 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "The [Escrow]." + ], + "writable": true + }, + { + "name": "creator", + "docs": [ + "Creator of the escrow." + ], + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "escrow_metadata", + "docs": [ + "The [ProposalMeta]." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119, + 95, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "escrow" + } + ] + } + }, + { + "name": "payer", + "docs": [ + "Payer of the [ProposalMeta]." + ], + "writable": true, + "signer": true + }, + { + "name": "system_program", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "CreateVestingEscrowMetadataParameters" + } + } + } + ] + }, + { + "name": "create_vesting_escrow_v2", + "docs": [ + "Create a vesting escrow for the given params", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* params - The params needed by instruction.", + "* vesting_start_time - The creation time of this escrow", + "* cliff_time - Trade cliff time of the escrow", + "* frequency - How frequent the claimable amount will be updated", + "* cliff_unlock_amount - The amount unlocked after cliff time", + "* amount_per_period - The amount unlocked per vesting period", + "* number_of_period - The total number of vesting period", + "* update_recipient_mode - Decide who can update the recipient of the escrow", + "* cancel_mode - Decide who can cancel the the escrow", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 181, + 155, + 104, + 183, + 182, + 128, + 35, + 47 + ], + "accounts": [ + { + "name": "base", + "docs": [ + "Base." + ], + "writable": true, + "signer": true + }, + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + } + ] + } + }, + { + "name": "token_mint" + }, + { + "name": "escrow_token", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "sender", + "docs": [ + "Sender." + ], + "writable": true, + "signer": true + }, + { + "name": "sender_token", + "docs": [ + "Sender Token Account." + ], + "writable": true + }, + { + "name": "recipient" + }, + { + "name": "token_program", + "docs": [ + "Token program." + ] + }, + { + "name": "system_program", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "CreateVestingEscrowParameters" + } + } + }, + { + "name": "remaining_accounts_info", + "type": { + "option": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "fund_root_escrow", + "docs": [ + "Fund root escrow" + ], + "discriminator": [ + 251, + 106, + 189, + 200, + 108, + 15, + 144, + 95 + ], + "accounts": [ + { + "name": "root_escrow", + "docs": [ + "Root Escrow." + ], + "writable": true + }, + { + "name": "token_mint", + "relations": [ + "root_escrow" + ] + }, + { + "name": "root_escrow_token", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "root_escrow" + }, + { + "kind": "account", + "path": "token_program" + }, + { + "kind": "account", + "path": "token_mint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "payer", + "docs": [ + "Payer." + ], + "writable": true, + "signer": true + }, + { + "name": "payer_token", + "docs": [ + "Payer Token Account." + ], + "writable": true + }, + { + "name": "token_program", + "docs": [ + "Token program." + ] + }, + { + "name": "system_program", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "associated_token_program", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "max_amount", + "type": "u64" + }, + { + "name": "remaining_accounts_info", + "type": { + "option": { + "defined": { + "name": "RemainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "update_vesting_escrow_recipient", + "docs": [ + "Update vesting escrow metadata", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* new_recipient - The address of the new recipient", + "* new_recipient_email - The email of the new recipient", + "" + ], + "discriminator": [ + 26, + 242, + 127, + 255, + 237, + 109, + 47, + 206 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "escrow_metadata", + "docs": [ + "Escrow metadata." + ], + "writable": true, + "optional": true + }, + { + "name": "signer", + "docs": [ + "Signer." + ], + "writable": true, + "signer": true + }, + { + "name": "system_program", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "event_authority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "new_recipient", + "type": "pubkey" + }, + { + "name": "new_recipient_email", + "type": { + "option": "string" + } + } + ] + } + ], + "accounts": [ + { + "name": "RootEscrow", + "discriminator": [ + 253, + 209, + 220, + 107, + 206, + 191, + 71, + 158 + ] + }, + { + "name": "VestingEscrow", + "discriminator": [ + 244, + 119, + 183, + 4, + 73, + 116, + 135, + 195 + ] + }, + { + "name": "VestingEscrowMetadata", + "discriminator": [ + 24, + 204, + 166, + 104, + 87, + 158, + 76, + 13 + ] + } + ], + "events": [ + { + "name": "EventCancelVestingEscrow", + "discriminator": [ + 113, + 2, + 117, + 173, + 195, + 39, + 101, + 155 + ] + }, + { + "name": "EventCancelVestingEscrowV3", + "discriminator": [ + 41, + 143, + 236, + 79, + 116, + 120, + 91, + 143 + ] + }, + { + "name": "EventClaim", + "discriminator": [ + 171, + 144, + 1, + 189, + 120, + 200, + 38, + 11 + ] + }, + { + "name": "EventClaimV3", + "discriminator": [ + 229, + 197, + 142, + 10, + 41, + 122, + 171, + 154 + ] + }, + { + "name": "EventCloseClaimStatus", + "discriminator": [ + 87, + 68, + 38, + 194, + 241, + 155, + 125, + 107 + ] + }, + { + "name": "EventCloseVestingEscrow", + "discriminator": [ + 45, + 141, + 253, + 209, + 196, + 133, + 21, + 204 + ] + }, + { + "name": "EventCreateRootEscrow", + "discriminator": [ + 105, + 216, + 97, + 182, + 27, + 224, + 199, + 228 + ] + }, + { + "name": "EventCreateVestingEscrow", + "discriminator": [ + 248, + 222, + 89, + 61, + 170, + 208, + 131, + 117 + ] + }, + { + "name": "EventFundRootEscrow", + "discriminator": [ + 74, + 8, + 68, + 181, + 198, + 235, + 138, + 81 + ] + }, + { + "name": "EventUpdateVestingEscrowRecipient", + "discriminator": [ + 206, + 218, + 33, + 65, + 133, + 237, + 131, + 57 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "MathOverflow", + "msg": "Math operation overflow" + }, + { + "code": 6001, + "name": "FrequencyIsZero", + "msg": "Frequency is zero" + }, + { + "code": 6002, + "name": "InvalidEscrowTokenAddress", + "msg": "Invalid escrow token address" + }, + { + "code": 6003, + "name": "InvalidUpdateRecipientMode", + "msg": "Invalid update recipient mode" + }, + { + "code": 6004, + "name": "InvalidCancelMode", + "msg": "Invalid cancel mode" + }, + { + "code": 6005, + "name": "NotPermitToDoThisAction", + "msg": "Not permit to do this action" + }, + { + "code": 6006, + "name": "InvalidRecipientTokenAccount", + "msg": "Invalid recipient token account" + }, + { + "code": 6007, + "name": "InvalidCreatorTokenAccount", + "msg": "Invalid creator token account" + }, + { + "code": 6008, + "name": "InvalidEscrowMetadata", + "msg": "Invalid escrow metadata" + }, + { + "code": 6009, + "name": "InvalidVestingStartTime", + "msg": "Invalid vesting start time" + }, + { + "code": 6010, + "name": "AlreadyCancelled", + "msg": "Already cancelled" + }, + { + "code": 6011, + "name": "CancelledAtIsZero", + "msg": "Cancelled timestamp is zero" + }, + { + "code": 6012, + "name": "IncorrectTokenProgramId", + "msg": "Invalid token program ID" + }, + { + "code": 6013, + "name": "TransferFeeCalculationFailure", + "msg": "Calculate transfer fee failure" + }, + { + "code": 6014, + "name": "UnsupportedMint", + "msg": "Unsupported mint" + }, + { + "code": 6015, + "name": "InvalidRemainingAccountSlice", + "msg": "Invalid remaining accounts" + }, + { + "code": 6016, + "name": "InsufficientRemainingAccounts", + "msg": "Insufficient remaining accounts" + }, + { + "code": 6017, + "name": "DuplicatedRemainingAccountTypes", + "msg": "Same accounts type is provided more than once" + }, + { + "code": 6018, + "name": "NoTransferHookProgram", + "msg": "Missing remaining accounts for transfer hook." + }, + { + "code": 6019, + "name": "ClaimingIsNotFinished", + "msg": "Claiming is not finished" + }, + { + "code": 6020, + "name": "InvalidMerkleProof", + "msg": "Invalid merkle proof" + }, + { + "code": 6021, + "name": "EscrowNotCancelled", + "msg": "Escrow is not cancelled" + }, + { + "code": 6022, + "name": "AmountIsZero", + "msg": "Amount is zero" + }, + { + "code": 6023, + "name": "InvalidParams", + "msg": "Invalid params" + } + ], + "types": [ + { + "name": "AccountsType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "TransferHookEscrow" + } + ] + } + }, + { + "name": "CreateRootEscrowParameters", + "type": { + "kind": "struct", + "fields": [ + { + "name": "max_claim_amount", + "type": "u64" + }, + { + "name": "max_escrow", + "type": "u64" + }, + { + "name": "version", + "type": "u64" + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "CreateVestingEscrowFromRootParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vesting_start_time", + "type": "u64" + }, + { + "name": "cliff_time", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliff_unlock_amount", + "type": "u64" + }, + { + "name": "amount_per_period", + "type": "u64" + }, + { + "name": "number_of_period", + "type": "u64" + }, + { + "name": "update_recipient_mode", + "type": "u8" + }, + { + "name": "cancel_mode", + "type": "u8" + } + ] + } + }, + { + "name": "CreateVestingEscrowMetadataParameters", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "description", + "type": "string" + }, + { + "name": "creator_email", + "type": "string" + }, + { + "name": "recipient_email", + "type": "string" + } + ] + } + }, + { + "name": "CreateVestingEscrowParameters", + "docs": [ + "Accounts for [locker::create_vesting_escrow]." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "vesting_start_time", + "type": "u64" + }, + { + "name": "cliff_time", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliff_unlock_amount", + "type": "u64" + }, + { + "name": "amount_per_period", + "type": "u64" + }, + { + "name": "number_of_period", + "type": "u64" + }, + { + "name": "update_recipient_mode", + "type": "u8" + }, + { + "name": "cancel_mode", + "type": "u8" + } + ] + } + }, + { + "name": "EventCancelVestingEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "signer", + "type": "pubkey" + }, + { + "name": "claimable_amount", + "type": "u64" + }, + { + "name": "remaining_amount", + "type": "u64" + }, + { + "name": "cancelled_at", + "type": "u64" + } + ] + } + }, + { + "name": "EventCancelVestingEscrowV3", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "signer", + "type": "pubkey" + }, + { + "name": "remaining_amount", + "type": "u64" + }, + { + "name": "cancelled_at", + "type": "u64" + } + ] + } + }, + { + "name": "EventClaim", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "current_ts", + "type": "u64" + }, + { + "name": "escrow", + "type": "pubkey" + } + ] + } + }, + { + "name": "EventClaimV3", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "current_ts", + "type": "u64" + }, + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "vesting_start_time", + "type": "u64" + }, + { + "name": "cliff_time", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliff_unlock_amount", + "type": "u64" + }, + { + "name": "amount_per_period", + "type": "u64" + }, + { + "name": "number_of_period", + "type": "u64" + }, + { + "name": "recipient", + "type": "pubkey" + } + ] + } + }, + { + "name": "EventCloseClaimStatus", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "recipient", + "type": "pubkey" + }, + { + "name": "rent_receiver", + "type": "pubkey" + } + ] + } + }, + { + "name": "EventCloseVestingEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + } + ] + } + }, + { + "name": "EventCreateRootEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "root_escrow", + "type": "pubkey" + }, + { + "name": "max_claim_amount", + "type": "u64" + }, + { + "name": "max_escrow", + "type": "u64" + }, + { + "name": "version", + "type": "u64" + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "EventCreateVestingEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vesting_start_time", + "type": "u64" + }, + { + "name": "cliff_time", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliff_unlock_amount", + "type": "u64" + }, + { + "name": "amount_per_period", + "type": "u64" + }, + { + "name": "number_of_period", + "type": "u64" + }, + { + "name": "update_recipient_mode", + "type": "u8" + }, + { + "name": "cancel_mode", + "type": "u8" + }, + { + "name": "recipient", + "type": "pubkey" + }, + { + "name": "escrow", + "type": "pubkey" + } + ] + } + }, + { + "name": "EventFundRootEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "root_escrow", + "type": "pubkey" + }, + { + "name": "funded_amount", + "type": "u64" + } + ] + } + }, + { + "name": "EventUpdateVestingEscrowRecipient", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "old_recipient", + "type": "pubkey" + }, + { + "name": "new_recipient", + "type": "pubkey" + }, + { + "name": "signer", + "type": "pubkey" + } + ] + } + }, + { + "name": "RemainingAccountsInfo", + "type": { + "kind": "struct", + "fields": [ + { + "name": "slices", + "type": { + "vec": { + "defined": { + "name": "RemainingAccountsSlice" + } + } + } + } + ] + } + }, + { + "name": "RemainingAccountsSlice", + "type": { + "kind": "struct", + "fields": [ + { + "name": "accounts_type", + "type": { + "defined": { + "name": "AccountsType" + } + } + }, + { + "name": "length", + "type": "u8" + } + ] + } + }, + { + "name": "RootEscrow", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "token_mint", + "docs": [ + "token mint" + ], + "type": "pubkey" + }, + { + "name": "creator", + "docs": [ + "creator of the escrow" + ], + "type": "pubkey" + }, + { + "name": "base", + "docs": [ + "escrow base key" + ], + "type": "pubkey" + }, + { + "name": "root", + "docs": [ + "256 bit merkle root" + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "bump", + "docs": [ + "bump" + ], + "type": "u8" + }, + { + "name": "token_program_flag", + "docs": [ + "token program flag" + ], + "type": "u8" + }, + { + "name": "padding_0", + "docs": [ + "padding" + ], + "type": { + "array": [ + "u8", + 6 + ] + } + }, + { + "name": "max_claim_amount", + "docs": [ + "max claim amount" + ], + "type": "u64" + }, + { + "name": "max_escrow", + "docs": [ + "max escrow" + ], + "type": "u64" + }, + { + "name": "total_funded_amount", + "docs": [ + "total funded amount" + ], + "type": "u64" + }, + { + "name": "total_escrow_created", + "docs": [ + "total escrow created" + ], + "type": "u64" + }, + { + "name": "total_distribute_amount", + "docs": [ + "total distributed amount" + ], + "type": "u64" + }, + { + "name": "version", + "docs": [ + "version" + ], + "type": "u64" + }, + { + "name": "padding", + "docs": [ + "padding" + ], + "type": "u64" + }, + { + "name": "buffer", + "docs": [ + "buffer" + ], + "type": { + "array": [ + "u128", + 5 + ] + } + } + ] + } + }, + { + "name": "VestingEscrow", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "recipient", + "docs": [ + "recipient address" + ], + "type": "pubkey" + }, + { + "name": "token_mint", + "docs": [ + "token mint" + ], + "type": "pubkey" + }, + { + "name": "creator", + "docs": [ + "creator of the escrow" + ], + "type": "pubkey" + }, + { + "name": "base", + "docs": [ + "escrow base key" + ], + "type": "pubkey" + }, + { + "name": "escrow_bump", + "docs": [ + "escrow bump" + ], + "type": "u8" + }, + { + "name": "update_recipient_mode", + "docs": [ + "update_recipient_mode" + ], + "type": "u8" + }, + { + "name": "cancel_mode", + "docs": [ + "cancel_mode" + ], + "type": "u8" + }, + { + "name": "token_program_flag", + "docs": [ + "token program flag" + ], + "type": "u8" + }, + { + "name": "padding_0", + "docs": [ + "padding" + ], + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "cliff_time", + "docs": [ + "cliff time" + ], + "type": "u64" + }, + { + "name": "frequency", + "docs": [ + "frequency" + ], + "type": "u64" + }, + { + "name": "cliff_unlock_amount", + "docs": [ + "cliff unlock amount" + ], + "type": "u64" + }, + { + "name": "amount_per_period", + "docs": [ + "amount per period" + ], + "type": "u64" + }, + { + "name": "number_of_period", + "docs": [ + "number of period" + ], + "type": "u64" + }, + { + "name": "total_claimed_amount", + "docs": [ + "total claimed amount" + ], + "type": "u64" + }, + { + "name": "vesting_start_time", + "docs": [ + "vesting start time" + ], + "type": "u64" + }, + { + "name": "cancelled_at", + "docs": [ + "cancelled_at" + ], + "type": "u64" + }, + { + "name": "padding_1", + "docs": [ + "buffer" + ], + "type": "u64" + }, + { + "name": "buffer", + "docs": [ + "buffer" + ], + "type": { + "array": [ + "u128", + 5 + ] + } + } + ] + } + }, + { + "name": "VestingEscrowMetadata", + "docs": [ + "Metadata about an escrow." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "docs": [ + "The [Escrow]." + ], + "type": "pubkey" + }, + { + "name": "name", + "docs": [ + "Name of escrow." + ], + "type": "string" + }, + { + "name": "description", + "docs": [ + "Description of escrow." + ], + "type": "string" + }, + { + "name": "creator_email", + "docs": [ + "Email of creator" + ], + "type": "string" + }, + { + "name": "recipient_email", + "docs": [ + "Email of recipient" + ], + "type": "string" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/target/types/locker.ts b/target/types/locker.ts new file mode 100644 index 0000000..f068081 --- /dev/null +++ b/target/types/locker.ts @@ -0,0 +1,3149 @@ +/** + * Program IDL in camelCase format in order to be used in JS/TS. + * + * Note that this is only a type helper and is not the actual IDL. The original + * IDL can be found at `target/idl/locker.json`. + */ +export type Locker = { + "address": "LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn", + "metadata": { + "name": "locker", + "version": "0.4.0", + "spec": "0.1.0", + "description": "Created with Anchor" + }, + "instructions": [ + { + "name": "cancelVestingEscrow", + "docs": [ + "Cancel a vesting escrow.", + "- The claimable token will be transferred to recipient", + "- The remaining token will be transferred to the creator", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 217, + 233, + 13, + 3, + 143, + 101, + 53, + 201 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "tokenMint", + "docs": [ + "Mint." + ], + "writable": true, + "relations": [ + "escrow" + ] + }, + { + "name": "escrowToken", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "creatorToken", + "docs": [ + "Creator Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "recipientToken", + "docs": [ + "Receipient Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "rentReceiver", + "docs": [ + "CHECKED: The Token Account will receive the rent" + ], + "writable": true + }, + { + "name": "signer", + "docs": [ + "Signer." + ], + "writable": true, + "signer": true + }, + { + "name": "memoProgram", + "docs": [ + "Memo program." + ], + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ] + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "remainingAccountsInfo", + "type": { + "option": { + "defined": { + "name": "remainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "claim", + "docs": [ + "Claim maximum amount from the vesting escrow", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* max_amount - The maximum amount claimed by the recipient", + "" + ], + "discriminator": [ + 62, + 198, + 214, + 193, + 213, + 159, + 108, + 210 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "escrowToken", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "escrow" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "recipient", + "docs": [ + "Recipient." + ], + "writable": true, + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "recipientToken", + "docs": [ + "Recipient Token Account." + ], + "writable": true + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "maxAmount", + "type": "u64" + } + ] + }, + { + "name": "claimV2", + "docs": [ + "Claim maximum amount from the vesting escrow", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* max_amount - The maximum amount claimed by the recipient", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 229, + 87, + 46, + 162, + 21, + 157, + 231, + 114 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "tokenMint", + "docs": [ + "Mint." + ], + "relations": [ + "escrow" + ] + }, + { + "name": "escrowToken", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "recipient", + "docs": [ + "Recipient." + ], + "writable": true, + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "recipientToken", + "docs": [ + "Recipient Token Account." + ], + "writable": true + }, + { + "name": "memoProgram", + "docs": [ + "Memo program." + ], + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ] + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "maxAmount", + "type": "u64" + }, + { + "name": "remainingAccountsInfo", + "type": { + "option": { + "defined": { + "name": "remainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "closeVestingEscrow", + "docs": [ + "Close vesting escrow", + "- Close vesting escrow and escrow ATA and escrow metadata if recipient already claimed all tokens", + "- Rent receiver must be escrow's creator", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 221, + 185, + 95, + 135, + 136, + 67, + 252, + 87 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "escrowMetadata", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119, + 95, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "escrow" + } + ] + } + }, + { + "name": "tokenMint", + "docs": [ + "Mint." + ], + "writable": true + }, + { + "name": "escrowToken", + "writable": true + }, + { + "name": "creatorToken", + "writable": true + }, + { + "name": "creator", + "docs": [ + "Creator." + ], + "writable": true, + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ] + }, + { + "name": "memoProgram", + "docs": [ + "Memo program." + ], + "address": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "remainingAccountsInfo", + "type": { + "option": { + "defined": { + "name": "remainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "createRootEscrow", + "docs": [ + "Create root escrow" + ], + "discriminator": [ + 116, + 212, + 12, + 188, + 77, + 226, + 32, + 201 + ], + "accounts": [ + { + "name": "base", + "signer": true + }, + { + "name": "rootEscrow", + "docs": [ + "Root Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 114, + 111, + 111, + 116, + 95, + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + }, + { + "kind": "account", + "path": "tokenMint" + }, + { + "kind": "arg", + "path": "params.version" + } + ] + } + }, + { + "name": "tokenMint" + }, + { + "name": "payer", + "docs": [ + "payer." + ], + "writable": true, + "signer": true + }, + { + "name": "creator" + }, + { + "name": "systemProgram", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "createRootEscrowParameters" + } + } + } + ] + }, + { + "name": "createVestingEscrow", + "docs": [ + "Create a vesting escrow for the given params", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* params - The params needed by instruction.", + "* vesting_start_time - The creation time of this escrow", + "* cliff_time - Trade cliff time of the escrow", + "* frequency - How frequent the claimable amount will be updated", + "* cliff_unlock_amount - The amount unlocked after cliff time", + "* amount_per_period - The amount unlocked per vesting period", + "* number_of_period - The total number of vesting period", + "* update_recipient_mode - Decide who can update the recipient of the escrow", + "* cancel_mode - Decide who can cancel the the escrow", + "" + ], + "discriminator": [ + 23, + 100, + 197, + 94, + 222, + 153, + 38, + 90 + ], + "accounts": [ + { + "name": "base", + "docs": [ + "Base." + ], + "writable": true, + "signer": true + }, + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + } + ] + } + }, + { + "name": "escrowToken", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "const", + "value": [ + 6, + 221, + 246, + 225, + 215, + 101, + 161, + 147, + 217, + 203, + 225, + 70, + 206, + 235, + 121, + 172, + 28, + 180, + 133, + 237, + 95, + 91, + 55, + 145, + 58, + 140, + 245, + 133, + 126, + 255, + 0, + 169 + ] + }, + { + "kind": "account", + "path": "sender_token.mint", + "account": "tokenAccount" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "sender", + "docs": [ + "Sender." + ], + "writable": true, + "signer": true + }, + { + "name": "senderToken", + "docs": [ + "Sender Token Account." + ], + "writable": true + }, + { + "name": "recipient" + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ], + "address": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + }, + { + "name": "systemProgram", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "createVestingEscrowParameters" + } + } + } + ] + }, + { + "name": "createVestingEscrowFromRoot", + "docs": [ + "Crate vesting escrow from root" + ], + "discriminator": [ + 6, + 238, + 161, + 108, + 252, + 114, + 246, + 91 + ], + "accounts": [ + { + "name": "rootEscrow", + "docs": [ + "Root Escrow." + ], + "writable": true + }, + { + "name": "base", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 98, + 97, + 115, + 101 + ] + }, + { + "kind": "account", + "path": "rootEscrow" + }, + { + "kind": "account", + "path": "recipient" + } + ] + } + }, + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + } + ] + } + }, + { + "name": "escrowToken", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "rootEscrowToken", + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "rootEscrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "tokenMint", + "docs": [ + "Mint." + ], + "relations": [ + "rootEscrow" + ] + }, + { + "name": "payer", + "docs": [ + "Rent Payer" + ], + "writable": true, + "signer": true + }, + { + "name": "recipient" + }, + { + "name": "systemProgram", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ] + }, + { + "name": "associatedTokenProgram", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "createVestingEscrowFromRootParams" + } + } + }, + { + "name": "proof", + "type": { + "vec": { + "array": [ + "u8", + 32 + ] + } + } + }, + { + "name": "remainingAccountsInfo", + "type": { + "option": { + "defined": { + "name": "remainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "createVestingEscrowMetadata", + "docs": [ + "Create vesting escrow metadata", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* params - The params needed by instruction.", + "* name - The name of the vesting escrow", + "* description - The description of the vesting escrow", + "* creator_email - The email of the creator", + "* recipient_email - The email of the recipient", + "" + ], + "discriminator": [ + 93, + 78, + 33, + 103, + 173, + 125, + 70, + 0 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "The [Escrow]." + ], + "writable": true + }, + { + "name": "creator", + "docs": [ + "Creator of the escrow." + ], + "signer": true, + "relations": [ + "escrow" + ] + }, + { + "name": "escrowMetadata", + "docs": [ + "The [ProposalMeta]." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119, + 95, + 109, + 101, + 116, + 97, + 100, + 97, + 116, + 97 + ] + }, + { + "kind": "account", + "path": "escrow" + } + ] + } + }, + { + "name": "payer", + "docs": [ + "Payer of the [ProposalMeta]." + ], + "writable": true, + "signer": true + }, + { + "name": "systemProgram", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "createVestingEscrowMetadataParameters" + } + } + } + ] + }, + { + "name": "createVestingEscrowV2", + "docs": [ + "Create a vesting escrow for the given params", + "This instruction supports both splToken and token2022", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* params - The params needed by instruction.", + "* vesting_start_time - The creation time of this escrow", + "* cliff_time - Trade cliff time of the escrow", + "* frequency - How frequent the claimable amount will be updated", + "* cliff_unlock_amount - The amount unlocked after cliff time", + "* amount_per_period - The amount unlocked per vesting period", + "* number_of_period - The total number of vesting period", + "* update_recipient_mode - Decide who can update the recipient of the escrow", + "* cancel_mode - Decide who can cancel the the escrow", + "* remaining_accounts_info: additional accounts needed by instruction", + "" + ], + "discriminator": [ + 181, + 155, + 104, + 183, + 182, + 128, + 35, + 47 + ], + "accounts": [ + { + "name": "base", + "docs": [ + "Base." + ], + "writable": true, + "signer": true + }, + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 101, + 115, + 99, + 114, + 111, + 119 + ] + }, + { + "kind": "account", + "path": "base" + } + ] + } + }, + { + "name": "tokenMint" + }, + { + "name": "escrowToken", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "escrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "sender", + "docs": [ + "Sender." + ], + "writable": true, + "signer": true + }, + { + "name": "senderToken", + "docs": [ + "Sender Token Account." + ], + "writable": true + }, + { + "name": "recipient" + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ] + }, + { + "name": "systemProgram", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "params", + "type": { + "defined": { + "name": "createVestingEscrowParameters" + } + } + }, + { + "name": "remainingAccountsInfo", + "type": { + "option": { + "defined": { + "name": "remainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "fundRootEscrow", + "docs": [ + "Fund root escrow" + ], + "discriminator": [ + 251, + 106, + 189, + 200, + 108, + 15, + 144, + 95 + ], + "accounts": [ + { + "name": "rootEscrow", + "docs": [ + "Root Escrow." + ], + "writable": true + }, + { + "name": "tokenMint", + "relations": [ + "rootEscrow" + ] + }, + { + "name": "rootEscrowToken", + "docs": [ + "Escrow Token Account." + ], + "writable": true, + "pda": { + "seeds": [ + { + "kind": "account", + "path": "rootEscrow" + }, + { + "kind": "account", + "path": "tokenProgram" + }, + { + "kind": "account", + "path": "tokenMint" + } + ], + "program": { + "kind": "const", + "value": [ + 140, + 151, + 37, + 143, + 78, + 36, + 137, + 241, + 187, + 61, + 16, + 41, + 20, + 142, + 13, + 131, + 11, + 90, + 19, + 153, + 218, + 255, + 16, + 132, + 4, + 142, + 123, + 216, + 219, + 233, + 248, + 89 + ] + } + } + }, + { + "name": "payer", + "docs": [ + "Payer." + ], + "writable": true, + "signer": true + }, + { + "name": "payerToken", + "docs": [ + "Payer Token Account." + ], + "writable": true + }, + { + "name": "tokenProgram", + "docs": [ + "Token program." + ] + }, + { + "name": "systemProgram", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "associatedTokenProgram", + "address": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "maxAmount", + "type": "u64" + }, + { + "name": "remainingAccountsInfo", + "type": { + "option": { + "defined": { + "name": "remainingAccountsInfo" + } + } + } + } + ] + }, + { + "name": "updateVestingEscrowRecipient", + "docs": [ + "Update vesting escrow metadata", + "# Arguments", + "", + "* ctx - The accounts needed by instruction.", + "* new_recipient - The address of the new recipient", + "* new_recipient_email - The email of the new recipient", + "" + ], + "discriminator": [ + 26, + 242, + 127, + 255, + 237, + 109, + 47, + 206 + ], + "accounts": [ + { + "name": "escrow", + "docs": [ + "Escrow." + ], + "writable": true + }, + { + "name": "escrowMetadata", + "docs": [ + "Escrow metadata." + ], + "writable": true, + "optional": true + }, + { + "name": "signer", + "docs": [ + "Signer." + ], + "writable": true, + "signer": true + }, + { + "name": "systemProgram", + "docs": [ + "system program." + ], + "address": "11111111111111111111111111111111" + }, + { + "name": "eventAuthority", + "pda": { + "seeds": [ + { + "kind": "const", + "value": [ + 95, + 95, + 101, + 118, + 101, + 110, + 116, + 95, + 97, + 117, + 116, + 104, + 111, + 114, + 105, + 116, + 121 + ] + } + ] + } + }, + { + "name": "program" + } + ], + "args": [ + { + "name": "newRecipient", + "type": "pubkey" + }, + { + "name": "newRecipientEmail", + "type": { + "option": "string" + } + } + ] + } + ], + "accounts": [ + { + "name": "rootEscrow", + "discriminator": [ + 253, + 209, + 220, + 107, + 206, + 191, + 71, + 158 + ] + }, + { + "name": "vestingEscrow", + "discriminator": [ + 244, + 119, + 183, + 4, + 73, + 116, + 135, + 195 + ] + }, + { + "name": "vestingEscrowMetadata", + "discriminator": [ + 24, + 204, + 166, + 104, + 87, + 158, + 76, + 13 + ] + } + ], + "events": [ + { + "name": "eventCancelVestingEscrow", + "discriminator": [ + 113, + 2, + 117, + 173, + 195, + 39, + 101, + 155 + ] + }, + { + "name": "eventCancelVestingEscrowV3", + "discriminator": [ + 41, + 143, + 236, + 79, + 116, + 120, + 91, + 143 + ] + }, + { + "name": "eventClaim", + "discriminator": [ + 171, + 144, + 1, + 189, + 120, + 200, + 38, + 11 + ] + }, + { + "name": "eventClaimV3", + "discriminator": [ + 229, + 197, + 142, + 10, + 41, + 122, + 171, + 154 + ] + }, + { + "name": "eventCloseClaimStatus", + "discriminator": [ + 87, + 68, + 38, + 194, + 241, + 155, + 125, + 107 + ] + }, + { + "name": "eventCloseVestingEscrow", + "discriminator": [ + 45, + 141, + 253, + 209, + 196, + 133, + 21, + 204 + ] + }, + { + "name": "eventCreateRootEscrow", + "discriminator": [ + 105, + 216, + 97, + 182, + 27, + 224, + 199, + 228 + ] + }, + { + "name": "eventCreateVestingEscrow", + "discriminator": [ + 248, + 222, + 89, + 61, + 170, + 208, + 131, + 117 + ] + }, + { + "name": "eventFundRootEscrow", + "discriminator": [ + 74, + 8, + 68, + 181, + 198, + 235, + 138, + 81 + ] + }, + { + "name": "eventUpdateVestingEscrowRecipient", + "discriminator": [ + 206, + 218, + 33, + 65, + 133, + 237, + 131, + 57 + ] + } + ], + "errors": [ + { + "code": 6000, + "name": "mathOverflow", + "msg": "Math operation overflow" + }, + { + "code": 6001, + "name": "frequencyIsZero", + "msg": "Frequency is zero" + }, + { + "code": 6002, + "name": "invalidEscrowTokenAddress", + "msg": "Invalid escrow token address" + }, + { + "code": 6003, + "name": "invalidUpdateRecipientMode", + "msg": "Invalid update recipient mode" + }, + { + "code": 6004, + "name": "invalidCancelMode", + "msg": "Invalid cancel mode" + }, + { + "code": 6005, + "name": "notPermitToDoThisAction", + "msg": "Not permit to do this action" + }, + { + "code": 6006, + "name": "invalidRecipientTokenAccount", + "msg": "Invalid recipient token account" + }, + { + "code": 6007, + "name": "invalidCreatorTokenAccount", + "msg": "Invalid creator token account" + }, + { + "code": 6008, + "name": "invalidEscrowMetadata", + "msg": "Invalid escrow metadata" + }, + { + "code": 6009, + "name": "invalidVestingStartTime", + "msg": "Invalid vesting start time" + }, + { + "code": 6010, + "name": "alreadyCancelled", + "msg": "Already cancelled" + }, + { + "code": 6011, + "name": "cancelledAtIsZero", + "msg": "Cancelled timestamp is zero" + }, + { + "code": 6012, + "name": "incorrectTokenProgramId", + "msg": "Invalid token program ID" + }, + { + "code": 6013, + "name": "transferFeeCalculationFailure", + "msg": "Calculate transfer fee failure" + }, + { + "code": 6014, + "name": "unsupportedMint", + "msg": "Unsupported mint" + }, + { + "code": 6015, + "name": "invalidRemainingAccountSlice", + "msg": "Invalid remaining accounts" + }, + { + "code": 6016, + "name": "insufficientRemainingAccounts", + "msg": "Insufficient remaining accounts" + }, + { + "code": 6017, + "name": "duplicatedRemainingAccountTypes", + "msg": "Same accounts type is provided more than once" + }, + { + "code": 6018, + "name": "noTransferHookProgram", + "msg": "Missing remaining accounts for transfer hook." + }, + { + "code": 6019, + "name": "claimingIsNotFinished", + "msg": "Claiming is not finished" + }, + { + "code": 6020, + "name": "invalidMerkleProof", + "msg": "Invalid merkle proof" + }, + { + "code": 6021, + "name": "escrowNotCancelled", + "msg": "Escrow is not cancelled" + }, + { + "code": 6022, + "name": "amountIsZero", + "msg": "Amount is zero" + }, + { + "code": 6023, + "name": "invalidParams", + "msg": "Invalid params" + } + ], + "types": [ + { + "name": "accountsType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "transferHookEscrow" + } + ] + } + }, + { + "name": "createRootEscrowParameters", + "type": { + "kind": "struct", + "fields": [ + { + "name": "maxClaimAmount", + "type": "u64" + }, + { + "name": "maxEscrow", + "type": "u64" + }, + { + "name": "version", + "type": "u64" + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "createVestingEscrowFromRootParams", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vestingStartTime", + "type": "u64" + }, + { + "name": "cliffTime", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliffUnlockAmount", + "type": "u64" + }, + { + "name": "amountPerPeriod", + "type": "u64" + }, + { + "name": "numberOfPeriod", + "type": "u64" + }, + { + "name": "updateRecipientMode", + "type": "u8" + }, + { + "name": "cancelMode", + "type": "u8" + } + ] + } + }, + { + "name": "createVestingEscrowMetadataParameters", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "description", + "type": "string" + }, + { + "name": "creatorEmail", + "type": "string" + }, + { + "name": "recipientEmail", + "type": "string" + } + ] + } + }, + { + "name": "createVestingEscrowParameters", + "docs": [ + "Accounts for [locker::create_vesting_escrow]." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "vestingStartTime", + "type": "u64" + }, + { + "name": "cliffTime", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliffUnlockAmount", + "type": "u64" + }, + { + "name": "amountPerPeriod", + "type": "u64" + }, + { + "name": "numberOfPeriod", + "type": "u64" + }, + { + "name": "updateRecipientMode", + "type": "u8" + }, + { + "name": "cancelMode", + "type": "u8" + } + ] + } + }, + { + "name": "eventCancelVestingEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "signer", + "type": "pubkey" + }, + { + "name": "claimableAmount", + "type": "u64" + }, + { + "name": "remainingAmount", + "type": "u64" + }, + { + "name": "cancelledAt", + "type": "u64" + } + ] + } + }, + { + "name": "eventCancelVestingEscrowV3", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "signer", + "type": "pubkey" + }, + { + "name": "remainingAmount", + "type": "u64" + }, + { + "name": "cancelledAt", + "type": "u64" + } + ] + } + }, + { + "name": "eventClaim", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "currentTs", + "type": "u64" + }, + { + "name": "escrow", + "type": "pubkey" + } + ] + } + }, + { + "name": "eventClaimV3", + "type": { + "kind": "struct", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "currentTs", + "type": "u64" + }, + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "vestingStartTime", + "type": "u64" + }, + { + "name": "cliffTime", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliffUnlockAmount", + "type": "u64" + }, + { + "name": "amountPerPeriod", + "type": "u64" + }, + { + "name": "numberOfPeriod", + "type": "u64" + }, + { + "name": "recipient", + "type": "pubkey" + } + ] + } + }, + { + "name": "eventCloseClaimStatus", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "recipient", + "type": "pubkey" + }, + { + "name": "rentReceiver", + "type": "pubkey" + } + ] + } + }, + { + "name": "eventCloseVestingEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + } + ] + } + }, + { + "name": "eventCreateRootEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "rootEscrow", + "type": "pubkey" + }, + { + "name": "maxClaimAmount", + "type": "u64" + }, + { + "name": "maxEscrow", + "type": "u64" + }, + { + "name": "version", + "type": "u64" + }, + { + "name": "root", + "type": { + "array": [ + "u8", + 32 + ] + } + } + ] + } + }, + { + "name": "eventCreateVestingEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "vestingStartTime", + "type": "u64" + }, + { + "name": "cliffTime", + "type": "u64" + }, + { + "name": "frequency", + "type": "u64" + }, + { + "name": "cliffUnlockAmount", + "type": "u64" + }, + { + "name": "amountPerPeriod", + "type": "u64" + }, + { + "name": "numberOfPeriod", + "type": "u64" + }, + { + "name": "updateRecipientMode", + "type": "u8" + }, + { + "name": "cancelMode", + "type": "u8" + }, + { + "name": "recipient", + "type": "pubkey" + }, + { + "name": "escrow", + "type": "pubkey" + } + ] + } + }, + { + "name": "eventFundRootEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "rootEscrow", + "type": "pubkey" + }, + { + "name": "fundedAmount", + "type": "u64" + } + ] + } + }, + { + "name": "eventUpdateVestingEscrowRecipient", + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "type": "pubkey" + }, + { + "name": "oldRecipient", + "type": "pubkey" + }, + { + "name": "newRecipient", + "type": "pubkey" + }, + { + "name": "signer", + "type": "pubkey" + } + ] + } + }, + { + "name": "remainingAccountsInfo", + "type": { + "kind": "struct", + "fields": [ + { + "name": "slices", + "type": { + "vec": { + "defined": { + "name": "remainingAccountsSlice" + } + } + } + } + ] + } + }, + { + "name": "remainingAccountsSlice", + "type": { + "kind": "struct", + "fields": [ + { + "name": "accountsType", + "type": { + "defined": { + "name": "accountsType" + } + } + }, + { + "name": "length", + "type": "u8" + } + ] + } + }, + { + "name": "rootEscrow", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "tokenMint", + "docs": [ + "token mint" + ], + "type": "pubkey" + }, + { + "name": "creator", + "docs": [ + "creator of the escrow" + ], + "type": "pubkey" + }, + { + "name": "base", + "docs": [ + "escrow base key" + ], + "type": "pubkey" + }, + { + "name": "root", + "docs": [ + "256 bit merkle root" + ], + "type": { + "array": [ + "u8", + 32 + ] + } + }, + { + "name": "bump", + "docs": [ + "bump" + ], + "type": "u8" + }, + { + "name": "tokenProgramFlag", + "docs": [ + "token program flag" + ], + "type": "u8" + }, + { + "name": "padding0", + "docs": [ + "padding" + ], + "type": { + "array": [ + "u8", + 6 + ] + } + }, + { + "name": "maxClaimAmount", + "docs": [ + "max claim amount" + ], + "type": "u64" + }, + { + "name": "maxEscrow", + "docs": [ + "max escrow" + ], + "type": "u64" + }, + { + "name": "totalFundedAmount", + "docs": [ + "total funded amount" + ], + "type": "u64" + }, + { + "name": "totalEscrowCreated", + "docs": [ + "total escrow created" + ], + "type": "u64" + }, + { + "name": "totalDistributeAmount", + "docs": [ + "total distributed amount" + ], + "type": "u64" + }, + { + "name": "version", + "docs": [ + "version" + ], + "type": "u64" + }, + { + "name": "padding", + "docs": [ + "padding" + ], + "type": "u64" + }, + { + "name": "buffer", + "docs": [ + "buffer" + ], + "type": { + "array": [ + "u128", + 5 + ] + } + } + ] + } + }, + { + "name": "vestingEscrow", + "serialization": "bytemuck", + "repr": { + "kind": "c" + }, + "type": { + "kind": "struct", + "fields": [ + { + "name": "recipient", + "docs": [ + "recipient address" + ], + "type": "pubkey" + }, + { + "name": "tokenMint", + "docs": [ + "token mint" + ], + "type": "pubkey" + }, + { + "name": "creator", + "docs": [ + "creator of the escrow" + ], + "type": "pubkey" + }, + { + "name": "base", + "docs": [ + "escrow base key" + ], + "type": "pubkey" + }, + { + "name": "escrowBump", + "docs": [ + "escrow bump" + ], + "type": "u8" + }, + { + "name": "updateRecipientMode", + "docs": [ + "updateRecipientMode" + ], + "type": "u8" + }, + { + "name": "cancelMode", + "docs": [ + "cancelMode" + ], + "type": "u8" + }, + { + "name": "tokenProgramFlag", + "docs": [ + "token program flag" + ], + "type": "u8" + }, + { + "name": "padding0", + "docs": [ + "padding" + ], + "type": { + "array": [ + "u8", + 4 + ] + } + }, + { + "name": "cliffTime", + "docs": [ + "cliff time" + ], + "type": "u64" + }, + { + "name": "frequency", + "docs": [ + "frequency" + ], + "type": "u64" + }, + { + "name": "cliffUnlockAmount", + "docs": [ + "cliff unlock amount" + ], + "type": "u64" + }, + { + "name": "amountPerPeriod", + "docs": [ + "amount per period" + ], + "type": "u64" + }, + { + "name": "numberOfPeriod", + "docs": [ + "number of period" + ], + "type": "u64" + }, + { + "name": "totalClaimedAmount", + "docs": [ + "total claimed amount" + ], + "type": "u64" + }, + { + "name": "vestingStartTime", + "docs": [ + "vesting start time" + ], + "type": "u64" + }, + { + "name": "cancelledAt", + "docs": [ + "cancelledAt" + ], + "type": "u64" + }, + { + "name": "padding1", + "docs": [ + "buffer" + ], + "type": "u64" + }, + { + "name": "buffer", + "docs": [ + "buffer" + ], + "type": { + "array": [ + "u128", + 5 + ] + } + } + ] + } + }, + { + "name": "vestingEscrowMetadata", + "docs": [ + "Metadata about an escrow." + ], + "type": { + "kind": "struct", + "fields": [ + { + "name": "escrow", + "docs": [ + "The [Escrow]." + ], + "type": "pubkey" + }, + { + "name": "name", + "docs": [ + "Name of escrow." + ], + "type": "string" + }, + { + "name": "description", + "docs": [ + "Description of escrow." + ], + "type": "string" + }, + { + "name": "creatorEmail", + "docs": [ + "Email of creator" + ], + "type": "string" + }, + { + "name": "recipientEmail", + "docs": [ + "Email of recipient" + ], + "type": "string" + } + ] + } + } + ] +}; -- 2.45.2 From 7c42a7cabba64d605d05a1928a2ad39a7b4c3dde Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Wed, 5 Feb 2025 20:12:09 +0530 Subject: [PATCH 02/16] Create lock using wsol --- next-env.d.ts | 1 - src/app/api/lock/route.ts | 207 ++++++++++---------------------------- src/utils/create-lock.ts | 8 +- 3 files changed, 55 insertions(+), 161 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index fd36f94..4f11a03 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -/// // NOTE: This file should not be edited // see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts index 422e0ae..fdd607a 100644 --- a/src/app/api/lock/route.ts +++ b/src/app/api/lock/route.ts @@ -1,172 +1,67 @@ import { NextRequest, NextResponse } from "next/server"; -import { Connection, Keypair, ParsedInstruction, ParsedTransactionWithMeta, PartiallyDecodedInstruction, PublicKey } from "@solana/web3.js"; +import { Connection, Keypair, PublicKey } from "@solana/web3.js"; import { createLock } from "../../../utils/create-lock"; import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import assert from "assert"; import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; -import { BN, min } from "bn.js"; +import { BN } from "bn.js"; +import Big from 'big.js'; -assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); +const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL!); -const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); -const MTM_MINT_ADDRESS = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT; - -function isParsedInstruction( - instruction: ParsedInstruction | PartiallyDecodedInstruction - ): instruction is ParsedInstruction { - return (instruction as ParsedInstruction).parsed !== undefined; +async function extractInfo(transactionSignature: string) { + const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed'); + if (!transaction) { + throw new Error('Transaction not found'); } -async function extractMTMTransferDetails( -connection: Connection, -signature: string, -mtmMintAddress: string // Mint address of the MTM token -) { -try { - // Fetch the transaction details using the signature - const transaction: ParsedTransactionWithMeta | null = await connection.getParsedTransaction(signature, { - maxSupportedTransactionVersion: 0, // Ensure compatibility with legacy transactions - }); + const transferInstruction = transaction.transaction.message.instructions.find( + (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) + ); - if (!transaction) { - throw new Error('Transaction not found'); - } + if (!transferInstruction || !('parsed' in transferInstruction)) { + throw new Error('Transfer instruction not found'); + } - // Extract the "from", "to", and "amount" for the MTM token transfer - let fromAddress: string | null = null; - let toAddress: string | null = null; - let tokenAmount: number | null = null; - - // Function to process instructions - const processInstructions = (instructions: ParsedInstruction[]) => { - instructions.forEach((instruction: ParsedInstruction) => { - if (instruction.programId.equals(new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'))) { - // Check if the instruction is a transfer instruction - if (instruction.parsed.type === 'transfer' || instruction.parsed.type === 'transferChecked') { - const parsedInfo = instruction.parsed.info; - const mint = parsedInfo.mint; - - // Check if the mint address matches the provided MTM mint address - if (mint === mtmMintAddress) { - fromAddress = parsedInfo.source; - toAddress = parsedInfo.destination; - tokenAmount = parsedInfo.amount; - } - } - } - }); - }; - - // Filter out PartiallyDecodedInstruction and process only ParsedInstruction - const parsedInstructions = transaction.transaction.message.instructions.filter(isParsedInstruction); - - // Process top-level instructions - processInstructions(parsedInstructions); - - // Process inner instructions (if any) - if (transaction.meta?.innerInstructions) { - transaction.meta.innerInstructions.forEach((inner) => { - const innerParsedInstructions = inner.instructions.filter(isParsedInstruction); - processInstructions(innerParsedInstructions); - }); - } - - if (!fromAddress || !toAddress || !tokenAmount) { - throw new Error('No matching MTM token transfer found in the transaction'); - } - - return { fromAddress, toAddress, tokenAmount }; -} catch (error) { - console.error('Error extracting MTM transfer details:', error); - throw error; + const { info: { amount, authority } } = transferInstruction.parsed; + return { authority, amount }; } + +async function createRewardLock(authority: string, amount: string) { + const { USER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; + if (!USER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { + throw new Error('Missing required environment variables'); + } + + const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); + const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); + const recipientPublicKey = new PublicKey(authority); + + const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_TOKEN_MINT}&vsToken=${WSOL_MINT}`; + const response = await fetch(url); + const { data } = await response.json(); + + const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_TOKEN_MINT].price).toFixed(9); + const mtmAmount = new Big(amount).div(new Big(10).pow(6)); + const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0)); + + return createLock(tokenLockerKeypair, recipientPublicKey, duration, wsolAmount); } export async function GET(req: NextRequest) { - try { - // Get signature from URL params - const { searchParams } = new URL(req.url); - const signature = searchParams.get('signature') || '4HBtnoNUuMGpmbhD9cPiJtbxkhux31pfZs3HYud5eopAU69RaC4UbJsYdj83eafFxV6eH8pSaRgqELrwyjrWp7yz'; - let amount = new BN(0); + try { + const { searchParams } = new URL(req.url); + const signature = searchParams.get('signature') || '4HBtnoNUuMGpmbhD9cPiJtbxkhux31pfZs3HYud5eopAU69RaC4UbJsYdj83eafFxV6eH8pSaRgqELrwyjrWp7yz'; - if (!signature) { - return NextResponse.json( - { error: "Transaction signature is required" }, - { status: 400 } - ); - } - - if (!MTM_MINT_ADDRESS) { - return NextResponse.json( - { error: "MTM_MINT_ADDRESS environment variable is not set" }, - { status: 500 } - ); - } - - // Extract transaction details - // const result = await extractMTMTransferDetails( - // connection, - // signature, - // MTM_MINT_ADDRESS - // ); - - // if (!result) { - // return NextResponse.json( - // { error: "Failed to extract transaction details" }, - // { status: 500 } - // ); - // } - - // console.log({ result }); - - // // Validate extracted values - // if (!result.fromAddress || result.tokenAmount <= 0) { - // return NextResponse.json( - // { error: "Invalid transaction details extracted" }, - // { status: 400 } - // ); - // } - - assert(process.env.USER_PRIVATE_KEY, 'USER_PRIVATE_KEY is required'); - assert(process.env.CLIFF_TIME, 'CLIFF_TIME is required'); - - const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY; - const CLIFF_TIME = process.env.CLIFF_TIME; - - const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); - const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); - const recipientPublicKey = new PublicKey('Bnnq8n3rRKZe8NJAYn4vBkxp1v8Bnc6zXpUPpDeujCu'); - - amount = amount.add(new BN(1000000)); - - // Call createLock function with extracted values - let escrow; - try { - escrow = await createLock(tokenLockerKeypair, recipientPublicKey, duration, amount); - } catch (error) { - console.error('Error creating lock:', error); - return NextResponse.json( - { error: "Failed to create lock" }, - { status: 500 } - ); - } - - console.log({ escrow }); - - // Return successful response - return NextResponse.json({ - success: true, - data: { - sender: recipientPublicKey, - amount: amount, - } - }); - - } catch (error) { - console.error('API route error:', error); - return NextResponse.json( - { error: "Internal server error" }, - { status: 500 } - ); + const { authority, amount } = await extractInfo(signature); + if (!authority || Number(amount) <= 0) { + return NextResponse.json({ error: "Invalid transaction details" }, { status: 400 }); } -} + + const escrow = await createRewardLock(authority, amount); + return NextResponse.json({ success: true, data: { escrow } }); + + } catch (error) { + console.error('API route error:', error); + return NextResponse.json({ error: "Internal server error" }, { status: 500 }); + } +} \ No newline at end of file diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index 8b540f6..ad300a0 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -14,16 +14,16 @@ import { createVestingPlanV2 } from '../locker-utils'; assert(process.env.RPC_ENDPOINT); assert(process.env.USER_PRIVATE_KEY); //assert(process.env.RECIPIENT_PUBLIC_KEY); -assert(process.env.MTM_MINT); +assert(process.env.WSOL_MINT); const RPC_ENDPOINT= process.env.RPC_ENDPOINT; -const MTM_MINT = process.env.MTM_MINT; +const WSOL_MINT = process.env.WSOL_MINT; const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY; const userKP = anchor.web3.Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); const connection = new Connection(RPC_ENDPOINT); -const token = new PublicKey(MTM_MINT); +const token = new PublicKey(WSOL_MINT); const provider = new anchor.AnchorProvider( connection, @@ -35,7 +35,7 @@ const provider = new anchor.AnchorProvider( anchor.setProvider(provider); export async function getMTMBalance (senderKeypair: anchor.web3.Keypair): Promise { - const mintPublicKey = new PublicKey(MTM_MINT); + const mintPublicKey = new PublicKey(WSOL_MINT); const publicKey = senderKeypair.publicKey; const tokenAccounts = await connection.getTokenAccountsByOwner(publicKey, { mint: mintPublicKey }); -- 2.45.2 From 12a6174b13b77c20a3ab5c93cc36ba9e313a05e8 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 6 Feb 2025 10:27:16 +0530 Subject: [PATCH 03/16] Implement transaction confirmation pattern --- src/locker-utils/index.ts | 21 +++++++++++++++------ src/utils/create-lock.ts | 3 +-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/locker-utils/index.ts b/src/locker-utils/index.ts index d732746..fef9b83 100644 --- a/src/locker-utils/index.ts +++ b/src/locker-utils/index.ts @@ -24,7 +24,7 @@ import { web3, workspace, } from '@coral-xyz/anchor'; -import { AccountMeta, Connection, SendTransactionError } from '@solana/web3.js'; +import { AccountMeta, Connection, TransactionExpiredTimeoutError } from '@solana/web3.js'; // TODO: Generate type file from IDL json import { Locker } from '../../target/types/locker'; @@ -140,7 +140,7 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { assert(tokenProgram); try { - const transaction = await program.methods + await program.methods .createVestingEscrowV2( { vestingStartTime, @@ -182,11 +182,20 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { return escrow; } catch (error) { - if (error instanceof SendTransactionError) { - console.error('Transaction failed:', error.message); - console.error('Logs:', error.logs); + if (error instanceof TransactionExpiredTimeoutError) { + console.error('Transaction confirmation delayed for',error.signature); + console.log('Confirming the transaction again...'); + const confirmedTransaction = await connection.getTransaction(error.signature, { + commitment: 'confirmed', + maxSupportedTransactionVersion: 0 + }); + + if(confirmedTransaction === null) { + console.error('Transaction failed for',error.signature); + } + + return escrow; } - throw error; } } diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index ad300a0..ac8748d 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -62,7 +62,6 @@ export async function getMTMBalance (senderKeypair: anchor.web3.Keypair): Promis }; export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipientPubKey: anchor.web3.PublicKey, duration: BN, balance: BN): Promise { - //const balance = await getMTMBalance(tokenLockerKeypair); if (balance.eq(new BN(0))) { console.log('No balance available to create lock, skipping...'); @@ -88,7 +87,7 @@ export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipi }); if (escrow) { - console.log('Lock created successfully: ', escrow.toString()); + console.log('Lock created successfully:',escrow.toString()); } return escrow; -- 2.45.2 From affab021a366a7e5fdc5b0ce30e60d16cfbd0ca4 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 6 Feb 2025 10:48:44 +0530 Subject: [PATCH 04/16] Revert to old uploadToPinata() --- package.json | 3 +-- src/app/api/flux/route.ts | 1 - src/app/api/lock/route.ts | 2 +- src/utils/create-lock.ts | 29 +------------------------ src/utils/uploadToPinata.ts | 43 ++++++++++--------------------------- 5 files changed, 14 insertions(+), 64 deletions(-) diff --git a/package.json b/package.json index 99aebb9..1412614 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,7 @@ "@coral-xyz/anchor": "^0.30.1", "@fal-ai/client": "^1.2.1", "@google/generative-ai": "^0.21.0", - "@pinata/sdk": "^2.1.0", - "@solana/spl-token": "^0.3.11", + "@solana/spl-token": "^0.3.8", "@solana/web3.js": "^1.78.4", "big.js": "^6.2.2", "bn.js": "^5.2.0", diff --git a/src/app/api/flux/route.ts b/src/app/api/flux/route.ts index 95a7082..afd9193 100644 --- a/src/app/api/flux/route.ts +++ b/src/app/api/flux/route.ts @@ -88,7 +88,6 @@ export async function POST(req: NextRequest): Promise { // Extract the image URL from the response const imageUrl = result.data?.images?.[0]?.url - console.log(imageUrl); if (!imageUrl) { console.error('No image URL in response:', result) diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts index fdd607a..3cf0f84 100644 --- a/src/app/api/lock/route.ts +++ b/src/app/api/lock/route.ts @@ -64,4 +64,4 @@ export async function GET(req: NextRequest) { console.error('API route error:', error); return NextResponse.json({ error: "Internal server error" }, { status: 500 }); } -} \ No newline at end of file +} diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index ac8748d..b7a05ab 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -34,33 +34,6 @@ const provider = new anchor.AnchorProvider( anchor.setProvider(provider); -export async function getMTMBalance (senderKeypair: anchor.web3.Keypair): Promise { - const mintPublicKey = new PublicKey(WSOL_MINT); - const publicKey = senderKeypair.publicKey; - const tokenAccounts = await connection.getTokenAccountsByOwner(publicKey, { mint: mintPublicKey }); - - let balance = new BN(0); - for (const tokenAccount of tokenAccounts.value) { - const accountInfo = await connection.getParsedAccountInfo( - tokenAccount.pubkey, - "confirmed" - ); - - if (!accountInfo.value || Buffer.isBuffer(accountInfo.value.data)) { - console.warn( - `Token account ${tokenAccount.pubkey.toBase58()} data is not parsed.` - ); - continue; - } - - const tokenAmount = - (accountInfo.value.data as any).parsed?.info?.tokenAmount?.amount || "0"; - balance = balance.add(new BN(tokenAmount)); - } - - return balance; -}; - export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipientPubKey: anchor.web3.PublicKey, duration: BN, balance: BN): Promise { if (balance.eq(new BN(0))) { @@ -82,7 +55,7 @@ export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipi numberOfPeriod: new BN(1), // Only release tokens once recipient: recipientPubKey, updateRecipientMode: 0, - cancelMode: 1, + cancelMode: 1, // Only creator can cancel the lock tokenProgram: TOKEN_PROGRAM_ID, }); diff --git a/src/utils/uploadToPinata.ts b/src/utils/uploadToPinata.ts index 3061d58..92c6a09 100644 --- a/src/utils/uploadToPinata.ts +++ b/src/utils/uploadToPinata.ts @@ -1,49 +1,28 @@ -import PinataClient from "@pinata/sdk" +import { PinataSDK } from 'pinata-web3'; import 'dotenv/config'; import assert from 'assert'; + import { FluxGenerationResult } from '../services/fluxService'; -import axios from 'axios'; -import FormData from 'form-data'; -assert(process.env.PINATA_API_KEY, "PINATA_API_KEY is required"); -assert(process.env.PINATA_SECRET_KEY, "PINATA_SECRET_KEY is required"); -assert(process.env.PINATA_JWT, "PINATA_SECRET_KEY is required"); -assert(process.env.PINATA_GATEWAY); +assert(process.env.PINATA_JWT, "PINATA_JWT is required"); +assert(process.env.PINATA_GATEWAY, "PINATA_GATEWAY is required"); -const pinata = new PinataClient({ - pinataApiKey: process.env.PINATA_API_KEY, - pinataSecretApiKey: process.env.PINATA_SECRET_KEY, - pinataJWTKey: process.env.PINATA_JWT +const pinata = new PinataSDK({ + pinataJwt: process.env.PINATA_JWT, + pinataGateway: process.env.PINATA_GATEWAY, }); - export async function uploadToPinata(imageUrl: string): Promise { try { - const response = await axios.get(imageUrl, { - responseType: 'arraybuffer' - }); + const upload = await pinata.upload.url(imageUrl); - // Create form data - const formData = new FormData(); - formData.append('file', Buffer.from(response.data), { - filename: `image-${Date.now()}.jpg`, - contentType: 'image/jpeg', - }); - - // Use the correct Pinata method for file upload - const result = await pinata.pinFileToIPFS(formData); - - if (!result.IpfsHash) { - throw new Error('IPFS hash not received from Pinata'); - } - - const publicURL = `${process.env.PINATA_GATEWAY}/ipfs/${result.IpfsHash}`; + const publicURL = await pinata.gateways.convert(upload.IpfsHash); return { imageUrl: publicURL }; } catch (error) { - console.error('Error uploading to Pinata:', error); + console.error('Error uploading to Pinata:', error) return { error: error instanceof Error ? error.message : 'Upload failed' }; } -} \ No newline at end of file +} -- 2.45.2 From d476f420663b1ac9d134429419566a255fd32b42 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 11:40:28 +0530 Subject: [PATCH 05/16] Round up MTM prices to nearest integer --- .env.example | 11 +++++++++-- src/app/api/lock/route.ts | 6 +++--- src/components/AIServiceCard.tsx | 12 ++++++------ src/locker-utils/index.ts | 8 ++++---- src/utils/create-lock.ts | 11 +++++------ 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/.env.example b/.env.example index 69de436..8edfffe 100644 --- a/.env.example +++ b/.env.example @@ -3,8 +3,8 @@ FAL_AI_KEY= NEXT_PUBLIC_MTM_TOKEN_MINT=97RggLo3zV5kFGYW4yoQTxr4Xkz4Vg2WPHzNYXXWpump NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY -NEXT_PUBLIC_SOLANA_RPC_URL=https://young-radial-orb.solana-mainnet.quiknode.pro/67612b364664616c29514e551bf5de38447ca3d4 -NEXT_PUBLIC_SOLANA_WEBSOCKET_URL=wss://young-radial-orb.solana-mainnet.quiknode.pro/67612b364664616c29514e551bf5de38447ca3d4 +NEXT_PUBLIC_SOLANA_RPC_URL=https://skilled-prettiest-seed.solana-mainnet.quiknode.pro/eeecfebd04e345f69f1900cc3483cbbfea02a158 +NEXT_PUBLIC_SOLANA_WEBSOCKET_URL=wss://skilled-prettiest-seed.solana-mainnet.quiknode.pro/eeecfebd04e345f69f1900cc3483cbbfea02a158 NEXT_PUBLIC_USDC_MINT=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v # Generate a key at https://app.pinata.cloud/developers/api-keys @@ -17,3 +17,10 @@ PINATA_GATEWAY= # For development: SITE_URL=http://localhost:3000 SITE_URL=https://memes.markto.market NEXT_PUBLIC_ACCOUNT_HANDLE= + +WSOL_LOCKER_PRIVATE_KEY= +WSOL_MINT=So11111111111111111111111111111111111111112 + +# Duration in seconds that WSOL will be locked for +CLIFF_TIME=172800 # 48 hours +REWARD_MULTIPLIER=4 diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts index 3cf0f84..65882bf 100644 --- a/src/app/api/lock/route.ts +++ b/src/app/api/lock/route.ts @@ -27,13 +27,13 @@ async function extractInfo(transactionSignature: string) { } async function createRewardLock(authority: string, amount: string) { - const { USER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; - if (!USER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { + const { WSOL_LOCKER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; + if (!WSOL_LOCKER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { throw new Error('Missing required environment variables'); } const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); - const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); + const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); const recipientPublicKey = new PublicKey(authority); const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_TOKEN_MINT}&vsToken=${WSOL_MINT}`; diff --git a/src/components/AIServiceCard.tsx b/src/components/AIServiceCard.tsx index f9e96e7..85bea1c 100644 --- a/src/components/AIServiceCard.tsx +++ b/src/components/AIServiceCard.tsx @@ -21,11 +21,11 @@ interface GenerationState { error: string | null } -const baseUnitToDecimalFormat = (value: BN, decimals: number): string => { +const baseUnitToWholeNumber = (value: BN, decimals: number): string => { const bigValue = new Big(value.toString()); const factor = new Big(10).pow(decimals); - return bigValue.div(factor).toFixed(decimals); + return bigValue.div(factor).round(0, Big.roundUp).toFixed(0); } const AIServiceCard: React.FC = ({ @@ -93,11 +93,11 @@ const AIServiceCard: React.FC = ({ const generateTwitterShareUrl = (imageUrl: string, transactionSignature: string): string => { const baseUrl = window.location.href; const cid = imageUrl.split("/image/")[1]; - const memeUrl = `${baseUrl}memes/${cid}`; + const memePageUrl = `${baseUrl}memes/${cid}`; const tweetText = `Check out this meme that I generated! \n TX Hash: '${transactionSignature}' \n @${process.env.NEXT_PUBLIC_ACCOUNT_HANDLE} \n`; - return `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(memeUrl)}`; + return `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(memePageUrl)}`; }; return ( @@ -109,7 +109,7 @@ const AIServiceCard: React.FC = ({

{description}

- Cost: {priceMTM ? baseUnitToDecimalFormat(priceMTM, 6) : '...'} MTM + Cost: {priceMTM ? baseUnitToWholeNumber(priceMTM, 6) : '...'} MTM
@@ -133,7 +133,7 @@ const AIServiceCard: React.FC = ({ transition-all duration-200 shadow-lg hover:shadow-green-500/25 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:shadow-none" > - {generationState.loading ? 'Processing...' : `Pay ${priceMTM ? baseUnitToDecimalFormat(priceMTM, 6) : '...'} MTM & Generate`} + {generationState.loading ? 'Processing...' : `Pay ${priceMTM ? baseUnitToWholeNumber(priceMTM, 6) : '...'} MTM & Generate`} diff --git a/src/locker-utils/index.ts b/src/locker-utils/index.ts index fef9b83..d8ab321 100644 --- a/src/locker-utils/index.ts +++ b/src/locker-utils/index.ts @@ -35,9 +35,9 @@ import { RemainingAccountsType, } from './token-2022/remaining-accounts'; -assert(process.env.RPC_ENDPOINT); +assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); -const connection = new Connection(process.env.RPC_ENDPOINT); +const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); const ESCROW_USE_SPL_TOKEN = 0; @@ -116,7 +116,7 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { ASSOCIATED_TOKEN_PROGRAM_ID ); - let remainingAccountsInfo = null; + let remainingAccountsInfo; let remainingAccounts: AccountMeta[] = []; if (tokenProgram == TOKEN_2022_PROGRAM_ID) { let inputTransferHookAccounts = @@ -225,7 +225,7 @@ export async function claimTokenV2(params: ClaimTokenParamsV2) { ASSOCIATED_TOKEN_PROGRAM_ID ); - let remainingAccountsInfo = null; + let remainingAccountsInfo; let remainingAccounts: AccountMeta[] | undefined = []; if (tokenProgram == TOKEN_2022_PROGRAM_ID) { let claimTransferHookAccounts = diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index b7a05ab..da5ac80 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -11,16 +11,15 @@ import { Connection, PublicKey } from "@solana/web3.js"; import { createVestingPlanV2 } from '../locker-utils'; -assert(process.env.RPC_ENDPOINT); -assert(process.env.USER_PRIVATE_KEY); -//assert(process.env.RECIPIENT_PUBLIC_KEY); +assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); +assert(process.env.WSOL_LOCKER_PRIVATE_KEY); assert(process.env.WSOL_MINT); -const RPC_ENDPOINT= process.env.RPC_ENDPOINT; +const RPC_ENDPOINT= process.env.NEXT_PUBLIC_SOLANA_RPC_URL; const WSOL_MINT = process.env.WSOL_MINT; -const USER_PRIVATE_KEY = process.env.USER_PRIVATE_KEY; +const WSOL_LOCKER_PRIVATE_KEY = process.env.WSOL_LOCKER_PRIVATE_KEY; -const userKP = anchor.web3.Keypair.fromSecretKey(bs58.decode(USER_PRIVATE_KEY)); +const userKP = anchor.web3.Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); const connection = new Connection(RPC_ENDPOINT); const token = new PublicKey(WSOL_MINT); -- 2.45.2 From 90cb89c9a0f8573198828245378e01fe28511836 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 12:07:00 +0530 Subject: [PATCH 06/16] Create lock on every fourth twitter post --- src/app/api/lock/route.ts | 47 ++------------------------------------ src/app/api/tweet/route.ts | 17 +++++++++----- src/utils/create-lock.ts | 42 +++++++++++++++++++++++++++++++++- src/utils/verifyPayment.ts | 3 +-- 4 files changed, 55 insertions(+), 54 deletions(-) diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts index 65882bf..ce4dc3c 100644 --- a/src/app/api/lock/route.ts +++ b/src/app/api/lock/route.ts @@ -1,52 +1,9 @@ import { NextRequest, NextResponse } from "next/server"; -import { Connection, Keypair, PublicKey } from "@solana/web3.js"; -import { createLock } from "../../../utils/create-lock"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { bs58 } from "@coral-xyz/anchor/dist/cjs/utils/bytes"; -import { BN } from "bn.js"; -import Big from 'big.js'; +import { Connection} from "@solana/web3.js"; +import { createRewardLock, extractInfo } from "../../../utils/create-lock"; const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL!); -async function extractInfo(transactionSignature: string) { - const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed'); - if (!transaction) { - throw new Error('Transaction not found'); - } - - const transferInstruction = transaction.transaction.message.instructions.find( - (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) - ); - - if (!transferInstruction || !('parsed' in transferInstruction)) { - throw new Error('Transfer instruction not found'); - } - - const { info: { amount, authority } } = transferInstruction.parsed; - return { authority, amount }; -} - -async function createRewardLock(authority: string, amount: string) { - const { WSOL_LOCKER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; - if (!WSOL_LOCKER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { - throw new Error('Missing required environment variables'); - } - - const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); - const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); - const recipientPublicKey = new PublicKey(authority); - - const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_TOKEN_MINT}&vsToken=${WSOL_MINT}`; - const response = await fetch(url); - const { data } = await response.json(); - - const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_TOKEN_MINT].price).toFixed(9); - const mtmAmount = new Big(amount).div(new Big(10).pow(6)); - const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0)); - - return createLock(tokenLockerKeypair, recipientPublicKey, duration, wsolAmount); -} - export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url); diff --git a/src/app/api/tweet/route.ts b/src/app/api/tweet/route.ts index f514ff9..5c661bc 100644 --- a/src/app/api/tweet/route.ts +++ b/src/app/api/tweet/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { saveTweet, verifySignatureInTweet } from '../../../utils/verifyTweet'; +import { createRewardLock, extractInfo } from '../../../utils/create-lock'; export async function POST(req: NextRequest): Promise { try { @@ -30,8 +31,17 @@ export async function POST(req: NextRequest): Promise { } const { isFourthUser } = await saveTweet({ transactionSignature: txSignature, url: memeUrl }); + if (isFourthUser) { - createTokenLockForRecipient(); + const { authority, amount } = await extractInfo(txSignature); + + if (!authority || Number(amount) <= 0) { + return NextResponse.json({ error: "Invalid transaction details" }, { status: 400 }); + } + + const escrow = await createRewardLock(authority, amount); + return NextResponse.json({ success: true, data: { escrow } }); + } return NextResponse.json({ success: isVerified, message: 'Tweet verified' }) @@ -59,8 +69,3 @@ const extractData = (tweet: string | object) => { handle: handleMatch ? handleMatch[1] : null, }; }; - -// TODO: Implement function to create lock for a recipient -const createTokenLockForRecipient = () => { - console.log('Lock created'); -} diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index da5ac80..4ada5e9 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -2,12 +2,13 @@ import assert from 'assert'; import BN from 'bn.js'; import 'dotenv/config'; import bs58 from 'bs58'; +import Big from 'big.js'; import * as anchor from "@coral-xyz/anchor"; import { TOKEN_PROGRAM_ID, } from "@solana/spl-token"; -import { Connection, PublicKey } from "@solana/web3.js"; +import { Connection, Keypair, PublicKey } from "@solana/web3.js"; import { createVestingPlanV2 } from '../locker-utils'; @@ -64,3 +65,42 @@ export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipi return escrow; } + +export async function extractInfo(transactionSignature: string) { + const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed'); + if (!transaction) { + throw new Error('Transaction not found'); + } + + const transferInstruction = transaction.transaction.message.instructions.find( + (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) + ); + + if (!transferInstruction || !('parsed' in transferInstruction)) { + throw new Error('Transfer instruction not found'); + } + + const { info: { amount, authority } } = transferInstruction.parsed; + return { authority, amount }; +} + +export async function createRewardLock(authority: string, amount: string) { + const { WSOL_LOCKER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; + if (!WSOL_LOCKER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { + throw new Error('Missing required environment variables'); + } + + const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); + const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); + const recipientPublicKey = new PublicKey(authority); + + const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_TOKEN_MINT}&vsToken=${WSOL_MINT}`; + const response = await fetch(url); + const { data } = await response.json(); + + const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_TOKEN_MINT].price).toFixed(9); + const mtmAmount = new Big(amount).div(new Big(10).pow(6)); + const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0)); + + return createLock(tokenLockerKeypair, recipientPublicKey, duration, wsolAmount); +} diff --git a/src/utils/verifyPayment.ts b/src/utils/verifyPayment.ts index 92986d8..63dc2e2 100644 --- a/src/utils/verifyPayment.ts +++ b/src/utils/verifyPayment.ts @@ -4,8 +4,6 @@ import BN from 'bn.js'; import { Connection } from '@solana/web3.js'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; -import { Payment } from '../entity/Payment'; - assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL; @@ -44,6 +42,7 @@ export async function markSignatureAsUsed(transactionSignature: string): Promise }); } +// TODO: Verify that payment receiver is correct export async function verifyPayment( transactionSignature: string, tokenAmount: BN, -- 2.45.2 From a1fb9f9083743164a8165656bfc4a1b24628ff74 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 14:22:30 +0530 Subject: [PATCH 07/16] Round up MTM while requesting payment --- package-lock.json | 380 ++-------------------------------------------- package.json | 1 + src/app/page.tsx | 16 +- 3 files changed, 25 insertions(+), 372 deletions(-) diff --git a/package-lock.json b/package-lock.json index b3adbb8..f99badf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,8 +11,7 @@ "@coral-xyz/anchor": "^0.30.1", "@fal-ai/client": "^1.2.1", "@google/generative-ai": "^0.21.0", - "@pinata/sdk": "^2.1.0", - "@solana/spl-token": "^0.3.11", + "@solana/spl-token": "^0.3.8", "@solana/web3.js": "^1.78.4", "big.js": "^6.2.2", "bn.js": "^5.2.0", @@ -26,6 +25,7 @@ "typeorm": "^0.3.12" }, "devDependencies": { + "@types/big.js": "^6.2.2", "@types/bn.js": "^5.1.6", "@types/node": "^20", "@types/react": "^18", @@ -698,53 +698,6 @@ "node": ">=10" } }, - "node_modules/@pinata/sdk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@pinata/sdk/-/sdk-2.1.0.tgz", - "integrity": "sha512-hkS0tcKtsjf9xhsEBs2Nbey5s+Db7x5rlOH9TaWHBXkJ7IwwOs2xnEDigNaxAHKjYAwcw+m2hzpO5QgOfeF7Zw==", - "deprecated": "Please install the new IPFS SDK at pinata-web3. More information at https://docs.pinata.cloud/web3/sdk", - "dependencies": { - "axios": "^0.21.1", - "form-data": "^2.3.3", - "is-ipfs": "^0.6.0", - "path": "^0.12.7" - } - }, - "node_modules/@pinata/sdk/node_modules/axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", - "dependencies": { - "follow-redirects": "^1.14.0" - } - }, - "node_modules/@pinata/sdk/node_modules/form-data": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.2.tgz", - "integrity": "sha512-GgwY0PS7DbXqajuGf4OYlsrIu3zgxD6Vvql43IBhm6MahqA5SK/7mwhtNj2AdH2z35YR34ujJ7BN+3fFC3jP5Q==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12", - "safe-buffer": "^5.2.1" - }, - "engines": { - "node": ">= 0.12" - } - }, - "node_modules/@pinata/sdk/node_modules/is-ipfs": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/is-ipfs/-/is-ipfs-0.6.3.tgz", - "integrity": "sha512-HyRot1dvLcxImtDqPxAaY1miO6WsiP/z7Yxpg2qpaLWv5UdhAPtLvHJ4kMLM0w8GSl8AFsVF23PHe1LzuWrUlQ==", - "dependencies": { - "bs58": "^4.0.1", - "cids": "~0.7.0", - "mafmt": "^7.0.0", - "multiaddr": "^7.2.1", - "multibase": "~0.6.0", - "multihashes": "~0.4.13" - } - }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -985,6 +938,13 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "devOptional": true }, + "node_modules/@types/big.js": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@types/big.js/-/big.js-6.2.2.tgz", + "integrity": "sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/bn.js": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz", @@ -2081,51 +2041,6 @@ "node": ">=10" } }, - "node_modules/cids": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.7.5.tgz", - "integrity": "sha512-zT7mPeghoWAu+ppn8+BS1tQ5qGmbMfB4AregnQjA/qHY3GC1m1ptI9GkWNlgeu38r7CuRdXB47uY2XgAYt6QVA==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "buffer": "^5.5.0", - "class-is": "^1.1.0", - "multibase": "~0.6.0", - "multicodec": "^1.0.0", - "multihashes": "~0.4.15" - }, - "engines": { - "node": ">=4.0.0", - "npm": ">=3.0.0" - } - }, - "node_modules/cids/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/class-is": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/class-is/-/class-is-1.1.0.tgz", - "integrity": "sha512-rhjH9AG1fvabIDoGRVH587413LPjTZgmDF9fOFCbFJQV4yuocX1mHxxvXI4g3cGwbVY9wAYIoKlg1N79frJKQw==" - }, "node_modules/clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", @@ -4191,14 +4106,6 @@ "node": ">= 12" } }, - "node_modules/ip-regex": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-4.3.0.tgz", - "integrity": "sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==", - "engines": { - "node": ">=8" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -4409,17 +4316,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-ip": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-ip/-/is-ip-3.1.0.tgz", - "integrity": "sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q==", - "dependencies": { - "ip-regex": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-ipfs": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/is-ipfs/-/is-ipfs-8.0.4.tgz", @@ -4952,14 +4848,6 @@ "node": ">=10" } }, - "node_modules/mafmt": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/mafmt/-/mafmt-7.1.0.tgz", - "integrity": "sha512-vpeo9S+hepT3k2h5iFxzEHvvR0GPBx9uKaErmnRzYNcaKb03DgOArjEMlgG4a9LcuZZ89a3I8xbeto487n26eA==", - "dependencies": { - "multiaddr": "^7.3.0" - } - }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -5183,226 +5071,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, - "node_modules/multiaddr": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/multiaddr/-/multiaddr-7.5.0.tgz", - "integrity": "sha512-GvhHsIGDULh06jyb6ev+VfREH9evJCFIRnh3jUt9iEZ6XDbyoisZRFEI9bMvK/AiR6y66y6P+eoBw9mBYMhMvw==", - "deprecated": "This module is deprecated, please upgrade to @multiformats/multiaddr", - "dependencies": { - "buffer": "^5.5.0", - "cids": "~0.8.0", - "class-is": "^1.1.0", - "is-ip": "^3.1.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - } - }, - "node_modules/multiaddr/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/multiaddr/node_modules/cids": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/cids/-/cids-0.8.3.tgz", - "integrity": "sha512-yoXTbV3llpm+EBGWKeL9xKtksPE/s6DPoDSY4fn8I8TEW1zehWXPSB0pwAXVDlLaOlrw+sNynj995uD9abmPhA==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "buffer": "^5.6.0", - "class-is": "^1.1.0", - "multibase": "^1.0.0", - "multicodec": "^1.0.1", - "multihashes": "^1.0.1" - }, - "engines": { - "node": ">=4.0.0", - "npm": ">=3.0.0" - } - }, - "node_modules/multiaddr/node_modules/cids/node_modules/multibase": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-1.0.1.tgz", - "integrity": "sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - }, - "engines": { - "node": ">=10.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/multiaddr/node_modules/multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "node_modules/multiaddr/node_modules/multihashes": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-1.0.1.tgz", - "integrity": "sha512-S27Tepg4i8atNiFaU5ZOm3+gl3KQlUanLs/jWcBxQHFttgq+5x1OgbQmf2d8axJ/48zYGBd/wT9d723USMFduw==", - "dependencies": { - "buffer": "^5.6.0", - "multibase": "^1.0.1", - "varint": "^5.0.0" - }, - "engines": { - "node": ">=10.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/multiaddr/node_modules/multihashes/node_modules/multibase": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-1.0.1.tgz", - "integrity": "sha512-KcCxpBVY8fdVKu4dJMAahq4F/2Z/9xqEjIiR7PiMe7LRGeorFn2NLmicN6nLBCqQvft6MG2Lc9X5P0IdyvnxEw==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - }, - "engines": { - "node": ">=10.0.0", - "npm": ">=6.0.0" - } - }, - "node_modules/multibase": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.6.1.tgz", - "integrity": "sha512-pFfAwyTjbbQgNc3G7D48JkJxWtoJoBMaR4xQUOuB8RnCgRqaYmWNFeJTTvrJ2w51bjLq2zTby6Rqj9TQ9elSUw==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, - "node_modules/multibase/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/multicodec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/multicodec/-/multicodec-1.0.4.tgz", - "integrity": "sha512-NDd7FeS3QamVtbgfvu5h7fd1IlbaC4EQ0/pgU4zqE2vdHCmBGsUa0TiM8/TdSeG6BMPC92OOCf8F1ocE/Wkrrg==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "buffer": "^5.6.0", - "varint": "^5.0.0" - } - }, - "node_modules/multicodec/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, "node_modules/multiformats": { "version": "13.3.1", "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-13.3.1.tgz", "integrity": "sha512-QxowxTNwJ3r5RMctoGA5p13w5RbRT2QDkoM+yFlqfLiioBp78nhDjnRLvmSBI9+KAqN4VdgOVWM9c0CHd86m3g==" }, - "node_modules/multihashes": { - "version": "0.4.21", - "resolved": "https://registry.npmjs.org/multihashes/-/multihashes-0.4.21.tgz", - "integrity": "sha512-uVSvmeCWf36pU2nB4/1kzYZjsXD9vofZKpgudqkceYY5g2aZZXJ5r9lxuzoRLl1OAp28XljXsEJ/X/85ZsKmKw==", - "dependencies": { - "buffer": "^5.5.0", - "multibase": "^0.7.0", - "varint": "^5.0.0" - } - }, - "node_modules/multihashes/node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/multihashes/node_modules/multibase": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/multibase/-/multibase-0.7.0.tgz", - "integrity": "sha512-TW8q03O0f6PNFTQDvh3xxH03c8CjGaaYrjkl9UQPG6rz53TQzzxJVCIWVjzcbN/Q5Y53Zd0IBQBMVktVgNx4Fg==", - "deprecated": "This module has been superseded by the multiformats module", - "dependencies": { - "base-x": "^3.0.8", - "buffer": "^5.5.0" - } - }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -5991,15 +5664,6 @@ "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, - "node_modules/path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", - "dependencies": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6336,14 +6000,6 @@ "node": ">= 0.8.0" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/progress-events": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/progress-events/-/progress-events-1.0.1.tgz", @@ -8140,24 +7796,11 @@ "node": ">=6.14.2" } }, - "node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dependencies": { - "inherits": "2.0.3" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "node_modules/util/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" - }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", @@ -8172,11 +7815,6 @@ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "devOptional": true }, - "node_modules/varint": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/varint/-/varint-5.0.2.tgz", - "integrity": "sha512-lKxKYG6H03yCZUpAGOPOsMcGxd1RHCu1iKvEHYDPmTyq2HueGhD73ssNBqqQWfvYs04G9iUFRvmAVLW20Jw6ow==" - }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index 1412614..1ed9a37 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "typeorm": "^0.3.12" }, "devDependencies": { + "@types/big.js": "^6.2.2", "@types/bn.js": "^5.1.6", "@types/node": "^20", "@types/react": "^18", diff --git a/src/app/page.tsx b/src/app/page.tsx index 5043c33..8d7fe42 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,6 +1,7 @@ 'use client' import React, { useState, useEffect } from 'react' +import Big from 'big.js'; import BN from 'bn.js'; import WalletHeader from '../components/WalletHeader' @@ -61,6 +62,19 @@ const Page: React.FC = (): React.ReactElement => { } } + const roundUpBN = (bnValue: BN) : BN => { + // Divide by 10^6 + const decimalBN = bnValue.div(new BN (10 ** 6)); + // Conver to Big decimal + const bigValue = new Big(decimalBN.toString()) + // Ceil the result + const ceiledValue = bigValue.add(1); + // Convert back to BN + const ceiledBN = new BN(ceiledValue.toString()); + // Multiple by 10^6 + return ceiledBN.mul(new BN (10 ** 6)) + } + const handleFluxGeneration = (modelId: string, cost: BN) => { return async (prompt: string): Promise => { const { connected, publicKey, type } = walletState; @@ -75,7 +89,7 @@ const Page: React.FC = (): React.ReactElement => { // Convert cost in USDC to MTM tokens using the price ratio const paymentResult = await processMTMPayment( publicKey, - cost, + roundUpBN(cost), type ) -- 2.45.2 From aa43f91b036a18afe406e381088600da09252ab6 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 14:44:14 +0530 Subject: [PATCH 08/16] Update env variables --- .env.example | 14 +++++++------- README.md | 2 +- quotes-service.ts | 8 ++++---- src/app/api/tweet/route.ts | 2 +- src/components/AIServiceCard.tsx | 2 +- src/services/paymentService.ts | 8 ++++---- src/utils/create-lock.ts | 24 ++++++++++++------------ 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.env.example b/.env.example index 8edfffe..720dbca 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,11 @@ # Get key from https://fal.ai FAL_AI_KEY= -NEXT_PUBLIC_MTM_TOKEN_MINT=97RggLo3zV5kFGYW4yoQTxr4Xkz4Vg2WPHzNYXXWpump -NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY +NEXT_PUBLIC_MTM_MINT_ADDRESS=97RggLo3zV5kFGYW4yoQTxr4Xkz4Vg2WPHzNYXXWpump +NEXT_PUBLIC_MTM_RECIPIENT_MULTISIG_ADDRESS=FFDx3SdAEeXrp6BTmStB4BDHpctGsaasZq4FFcowRobY NEXT_PUBLIC_SOLANA_RPC_URL=https://skilled-prettiest-seed.solana-mainnet.quiknode.pro/eeecfebd04e345f69f1900cc3483cbbfea02a158 NEXT_PUBLIC_SOLANA_WEBSOCKET_URL=wss://skilled-prettiest-seed.solana-mainnet.quiknode.pro/eeecfebd04e345f69f1900cc3483cbbfea02a158 -NEXT_PUBLIC_USDC_MINT=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v +NEXT_PUBLIC_USDC_MINT_ADDRESS=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v # Generate a key at https://app.pinata.cloud/developers/api-keys PINATA_JWT= @@ -16,11 +16,11 @@ PINATA_GATEWAY= # Change to your website URL # For development: SITE_URL=http://localhost:3000 SITE_URL=https://memes.markto.market -NEXT_PUBLIC_ACCOUNT_HANDLE= +NEXT_PUBLIC_TWITTER_HANDLE= -WSOL_LOCKER_PRIVATE_KEY= -WSOL_MINT=So11111111111111111111111111111111111111112 +WSOL_LOCKER_ACCOUNT_PK= +WSOL_MINT_ADDRESS=So11111111111111111111111111111111111111112 # Duration in seconds that WSOL will be locked for -CLIFF_TIME=172800 # 48 hours +WSOL_LOCK_DURATION_IN_SECONDS=172800 # 48 hours REWARD_MULTIPLIER=4 diff --git a/README.md b/README.md index 2236ed4..0472fc5 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ This project is a Solana-based meme generator that allows users to connect their PINATA_GATEWAY= # Add the account handle to be set in the tweet - NEXT_PUBLIC_ACCOUNT_HANDLE= + NEXT_PUBLIC_TWITTER_HANDLE= ``` - Run the development server: diff --git a/quotes-service.ts b/quotes-service.ts index a16ad43..c474af9 100644 --- a/quotes-service.ts +++ b/quotes-service.ts @@ -3,11 +3,11 @@ import BN from "bn.js"; import fetch from 'node-fetch'; import Big from 'big.js'; -assert(process.env.NEXT_PUBLIC_USDC_MINT, 'USDC_MINT is required'); -assert(process.env.NEXT_PUBLIC_MTM_TOKEN_MINT, 'MTM_TOKEN_MINT is required'); +assert(process.env.NEXT_PUBLIC_USDC_MINT_ADDRESS, 'USDC_MINT_ADDRESS is required'); +assert(process.env.NEXT_PUBLIC_MTM_MINT_ADDRESS, 'MTM_MINT_ADDRESS is required'); -const MTM_TOKEN_MINT = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT; -const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT; +const MTM_TOKEN_MINT = process.env.NEXT_PUBLIC_MTM_MINT_ADDRESS; +const USDC_MINT = process.env.NEXT_PUBLIC_USDC_MINT_ADDRESS; class QuotesService { // Stores the MTM amount for 1 USDC diff --git a/src/app/api/tweet/route.ts b/src/app/api/tweet/route.ts index 5c661bc..9bff4a4 100644 --- a/src/app/api/tweet/route.ts +++ b/src/app/api/tweet/route.ts @@ -22,7 +22,7 @@ export async function POST(req: NextRequest): Promise { } const isSigVerified = await verifySignatureInTweet(txSignature); - const isHandleCorrect = handle === process.env.NEXT_PUBLIC_ACCOUNT_HANDLE; + const isHandleCorrect = handle === process.env.NEXT_PUBLIC_TWITTER_HANDLE; // TODO: Verify dynamic page URL in tweet const isVerified = isSigVerified && isHandleCorrect; diff --git a/src/components/AIServiceCard.tsx b/src/components/AIServiceCard.tsx index 85bea1c..73638cc 100644 --- a/src/components/AIServiceCard.tsx +++ b/src/components/AIServiceCard.tsx @@ -95,7 +95,7 @@ const AIServiceCard: React.FC = ({ const cid = imageUrl.split("/image/")[1]; const memePageUrl = `${baseUrl}memes/${cid}`; - const tweetText = `Check out this meme that I generated! \n TX Hash: '${transactionSignature}' \n @${process.env.NEXT_PUBLIC_ACCOUNT_HANDLE} \n`; + const tweetText = `Check out this meme that I generated! \n TX Hash: '${transactionSignature}' \n @${process.env.NEXT_PUBLIC_TWITTER_HANDLE} \n`; return `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(memePageUrl)}`; }; diff --git a/src/services/paymentService.ts b/src/services/paymentService.ts index 6e809a2..7bdcba1 100644 --- a/src/services/paymentService.ts +++ b/src/services/paymentService.ts @@ -12,11 +12,11 @@ import { import { WalletType } from './types' assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); -assert(process.env.NEXT_PUBLIC_MTM_TOKEN_MINT, 'MTM_TOKEN_MINT is required'); -assert(process.env.NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS, 'PAYMENT_RECEIVER_ADDRESS is required'); +assert(process.env.NEXT_PUBLIC_MTM_MINT_ADDRESS, 'MTM_MINT_ADDRESS is required'); +assert(process.env.NEXT_PUBLIC_MTM_RECIPIENT_MULTISIG_ADDRESS, 'MTM_RECIPIENT_MULTISIG_ADDRESS is required'); -const MTM_TOKEN_MINT = process.env.NEXT_PUBLIC_MTM_TOKEN_MINT; -const PAYMENT_RECEIVER_ADDRESS = process.env.NEXT_PUBLIC_PAYMENT_RECEIVER_ADDRESS; +const MTM_TOKEN_MINT = process.env.NEXT_PUBLIC_MTM_MINT_ADDRESS; +const PAYMENT_RECEIVER_ADDRESS = process.env.NEXT_PUBLIC_MTM_RECIPIENT_MULTISIG_ADDRESS; const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL; const SOLANA_WEBSOCKET_URL = process.env.NEXT_PUBLIC_SOLANA_WEBSOCKET_URL; diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index 4ada5e9..ca67d16 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -13,17 +13,17 @@ import { Connection, Keypair, PublicKey } from "@solana/web3.js"; import { createVestingPlanV2 } from '../locker-utils'; assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); -assert(process.env.WSOL_LOCKER_PRIVATE_KEY); -assert(process.env.WSOL_MINT); +assert(process.env.WSOL_LOCKER_ACCOUNT_PK); +assert(process.env.WSOL_MINT_ADDRESS); const RPC_ENDPOINT= process.env.NEXT_PUBLIC_SOLANA_RPC_URL; -const WSOL_MINT = process.env.WSOL_MINT; -const WSOL_LOCKER_PRIVATE_KEY = process.env.WSOL_LOCKER_PRIVATE_KEY; +const WSOL_MINT_ADDRESS = process.env.WSOL_MINT_ADDRESS; +const WSOL_LOCKER_ACCOUNT_PK = process.env.WSOL_LOCKER_ACCOUNT_PK; -const userKP = anchor.web3.Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); +const userKP = anchor.web3.Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_ACCOUNT_PK)); const connection = new Connection(RPC_ENDPOINT); -const token = new PublicKey(WSOL_MINT); +const token = new PublicKey(WSOL_MINT_ADDRESS); const provider = new anchor.AnchorProvider( connection, @@ -85,20 +85,20 @@ export async function extractInfo(transactionSignature: string) { } export async function createRewardLock(authority: string, amount: string) { - const { WSOL_LOCKER_PRIVATE_KEY, CLIFF_TIME, WSOL_MINT, NEXT_PUBLIC_MTM_TOKEN_MINT, REWARD_MULTIPLIER } = process.env; - if (!WSOL_LOCKER_PRIVATE_KEY || !CLIFF_TIME || !WSOL_MINT || !NEXT_PUBLIC_MTM_TOKEN_MINT || !REWARD_MULTIPLIER) { + const { WSOL_LOCKER_ACCOUNT_PK, WSOL_LOCK_DURATION_IN_SECONDS, WSOL_MINT_ADDRESS, NEXT_PUBLIC_MTM_MINT_ADDRESS, REWARD_MULTIPLIER } = process.env; + if (!WSOL_LOCKER_ACCOUNT_PK || !WSOL_LOCK_DURATION_IN_SECONDS || !WSOL_MINT_ADDRESS || !NEXT_PUBLIC_MTM_MINT_ADDRESS || !REWARD_MULTIPLIER) { throw new Error('Missing required environment variables'); } - const duration = new BN(CLIFF_TIME).add(new BN(Math.floor(Date.now() / 1000))); - const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_PRIVATE_KEY)); + const duration = new BN(WSOL_LOCK_DURATION_IN_SECONDS).add(new BN(Math.floor(Date.now() / 1000))); + const tokenLockerKeypair = Keypair.fromSecretKey(bs58.decode(WSOL_LOCKER_ACCOUNT_PK)); const recipientPublicKey = new PublicKey(authority); - const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_TOKEN_MINT}&vsToken=${WSOL_MINT}`; + const url = `https://api.jup.ag/price/v2?ids=${NEXT_PUBLIC_MTM_MINT_ADDRESS}&vsToken=${WSOL_MINT_ADDRESS}`; const response = await fetch(url); const { data } = await response.json(); - const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_TOKEN_MINT].price).toFixed(9); + const priceWSOLFor1MTM = new Big(data[NEXT_PUBLIC_MTM_MINT_ADDRESS].price).toFixed(9); const mtmAmount = new Big(amount).div(new Big(10).pow(6)); const wsolAmount = new BN(new Big(mtmAmount).times(priceWSOLFor1MTM).times(new Big(10).pow(9)).times(REWARD_MULTIPLIER).toFixed(0)); -- 2.45.2 From 299e06f769a23b435cccac6516e994fe74c72396 Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Thu, 6 Feb 2025 15:45:44 +0530 Subject: [PATCH 09/16] Rollback tweet save if verification fails --- src/app/api/tweet/route.ts | 20 ++++---------------- src/locker-utils/index.ts | 2 ++ src/utils/verifyTweet.ts | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/app/api/tweet/route.ts b/src/app/api/tweet/route.ts index 9bff4a4..dd27fe8 100644 --- a/src/app/api/tweet/route.ts +++ b/src/app/api/tweet/route.ts @@ -1,7 +1,6 @@ import { NextRequest, NextResponse } from 'next/server'; -import { saveTweet, verifySignatureInTweet } from '../../../utils/verifyTweet'; -import { createRewardLock, extractInfo } from '../../../utils/create-lock'; +import { processTweet, verifySignatureInTweet } from '../../../utils/verifyTweet'; export async function POST(req: NextRequest): Promise { try { @@ -30,21 +29,10 @@ export async function POST(req: NextRequest): Promise { throw new Error('Tweet is not valid'); } - const { isFourthUser } = await saveTweet({ transactionSignature: txSignature, url: memeUrl }); + // Verify and store valid tweet + const result = await processTweet(txSignature, memeUrl); - if (isFourthUser) { - const { authority, amount } = await extractInfo(txSignature); - - if (!authority || Number(amount) <= 0) { - return NextResponse.json({ error: "Invalid transaction details" }, { status: 400 }); - } - - const escrow = await createRewardLock(authority, amount); - return NextResponse.json({ success: true, data: { escrow } }); - - } - - return NextResponse.json({ success: isVerified, message: 'Tweet verified' }) + return NextResponse.json(result) } catch (error) { console.error('Error while verifying tweet:', error) return NextResponse.json( diff --git a/src/locker-utils/index.ts b/src/locker-utils/index.ts index d8ab321..29b367f 100644 --- a/src/locker-utils/index.ts +++ b/src/locker-utils/index.ts @@ -196,6 +196,8 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { return escrow; } + + throw new Error; } } diff --git a/src/utils/verifyTweet.ts b/src/utils/verifyTweet.ts index c7ddc5e..26c701d 100644 --- a/src/utils/verifyTweet.ts +++ b/src/utils/verifyTweet.ts @@ -1,4 +1,7 @@ +import { DataSource, EntityTarget } from 'typeorm'; + import { Tweet } from '../entity/Tweet'; +import { createRewardLock, extractInfo } from './create-lock'; export async function verifySignatureInTweet(transactionSignature: string): Promise { const paymentRepository = global.appDataSource.getRepository(global.entities.Payment); @@ -18,14 +21,35 @@ export async function verifySignatureInTweet(transactionSignature: string): Prom return true; } -export async function saveTweet(data: Partial): Promise<{ isFourthUser: boolean }> { - return await global.appDataSource.transaction(async (transactionalEntityManager) => { - const tweetRepository = transactionalEntityManager.getRepository(global.entities.Tweet); +export async function processTweet(txSignature: string, memeUrl: string | null) { + return await (global.appDataSource as DataSource).transaction(async (transactionalEntityManager) => { + const tweetRepository = transactionalEntityManager.getRepository(global.entities.Tweet as EntityTarget); - const tweet = await tweetRepository.save(data); + const tweet = await tweetRepository.save({ + transactionSignature: txSignature, + url: memeUrl, + }); - return { - isFourthUser: tweet.id % 4 === 0 - }; + const isFourthUser = tweet.id % 4 === 0; + + try { + if (isFourthUser) { + const { authority, amount } = await extractInfo(txSignature); + + if (!authority || Number(amount) <= 0) { + return { error: "Invalid transaction details" } + } + + const escrow = await createRewardLock(authority, amount); + + return { success: true, data: { escrow } }; + } + + return { success: true, message: 'Tweet verified' } + } catch (error) { + console.error('Error locking tokens.'); + + throw new Error("Transaction failed."); + } }); } -- 2.45.2 From bfda39830ab72be0fa2aed06ff98a091d1bc180d Mon Sep 17 00:00:00 2001 From: IshaVenikar Date: Thu, 6 Feb 2025 16:00:41 +0530 Subject: [PATCH 10/16] Refactor out tweet text generation and regex logic --- src/app/api/tweet/route.ts | 17 +---------------- src/components/AIServiceCard.tsx | 4 +++- src/utils/tweetMessage.ts | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 src/utils/tweetMessage.ts diff --git a/src/app/api/tweet/route.ts b/src/app/api/tweet/route.ts index dd27fe8..26a0be3 100644 --- a/src/app/api/tweet/route.ts +++ b/src/app/api/tweet/route.ts @@ -1,6 +1,7 @@ import { NextRequest, NextResponse } from 'next/server'; import { processTweet, verifySignatureInTweet } from '../../../utils/verifyTweet'; +import { extractData } from '../../../utils/tweetMessage'; export async function POST(req: NextRequest): Promise { try { @@ -41,19 +42,3 @@ export async function POST(req: NextRequest): Promise { ) } } - -const extractData = (tweet: string | object) => { - const tweetText = typeof tweet === 'string' ? tweet : JSON.stringify(tweet); - - const decodedTweet = tweetText.replace(/'/g, "'").replace(/"/g, '"'); - - const urlMatch = decodedTweet.match(//); - const txSignatureMatch = decodedTweet.match(/TX Hash: '([^']+)'/); - const handleMatch = decodedTweet.match(/@([A-Za-z0-9_]+)/); - - return { - memeUrl: urlMatch ? urlMatch[1] : null, - txSignature: txSignatureMatch ? txSignatureMatch[1].trim() : null, - handle: handleMatch ? handleMatch[1] : null, - }; -}; diff --git a/src/components/AIServiceCard.tsx b/src/components/AIServiceCard.tsx index 73638cc..52d94dd 100644 --- a/src/components/AIServiceCard.tsx +++ b/src/components/AIServiceCard.tsx @@ -5,6 +5,8 @@ import BN from 'bn.js'; import Big from 'big.js'; import dynamic from 'next/dynamic' +import { generateTweetText } from '../utils/tweetMessage'; + interface AIServiceCardProps { title: string description: string @@ -95,7 +97,7 @@ const AIServiceCard: React.FC = ({ const cid = imageUrl.split("/image/")[1]; const memePageUrl = `${baseUrl}memes/${cid}`; - const tweetText = `Check out this meme that I generated! \n TX Hash: '${transactionSignature}' \n @${process.env.NEXT_PUBLIC_TWITTER_HANDLE} \n`; + const tweetText = generateTweetText(transactionSignature, process.env.NEXT_PUBLIC_TWITTER_HANDLE) return `https://twitter.com/intent/tweet?text=${encodeURIComponent(tweetText)}&url=${encodeURIComponent(memePageUrl)}`; }; diff --git a/src/utils/tweetMessage.ts b/src/utils/tweetMessage.ts new file mode 100644 index 0000000..77eb453 --- /dev/null +++ b/src/utils/tweetMessage.ts @@ -0,0 +1,19 @@ +export const generateTweetText = (transactionSignature: string, handle: string | undefined) => { + return `Check out this meme that I generated! \n TX Hash: '${transactionSignature}' \n @${handle} \n`; +}; + +export const extractData = (tweet: string | object) => { + const tweetText = typeof tweet === 'string' ? tweet : JSON.stringify(tweet); + + const decodedTweet = tweetText.replace(/'/g, "'").replace(/"/g, '"'); + + const urlMatch = decodedTweet.match(//); + const txSignatureMatch = decodedTweet.match(/TX Hash: '([^']+)'/); + const handleMatch = decodedTweet.match(/@([A-Za-z0-9_]+)/); + + return { + memeUrl: urlMatch ? urlMatch[1] : null, + txSignature: txSignatureMatch ? txSignatureMatch[1].trim() : null, + handle: handleMatch ? handleMatch[1] : null, + }; +}; -- 2.45.2 From e04c43baca659a10ef550b89b7dc8f53abbe192c Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 16:13:45 +0530 Subject: [PATCH 11/16] Update type for remainingAccountsInfo --- package-lock.json | 12 ++++++++++++ package.json | 1 + src/locker-utils/index.ts | 7 +++---- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index f99badf..fc7c87f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "devDependencies": { "@types/big.js": "^6.2.2", "@types/bn.js": "^5.1.6", + "@types/bs58": "^4.0.4", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", @@ -954,6 +955,17 @@ "@types/node": "*" } }, + "node_modules/@types/bs58": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/bs58/-/bs58-4.0.4.tgz", + "integrity": "sha512-0IEpMFXXQi2zXaXl9GJ3sRwQo0uEkD+yFOv+FnAU5lkPtcu6h61xb7jc2CFPEZ5BUOaiP13ThuGc9HD4R8lR5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "base-x": "^3.0.6" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", diff --git a/package.json b/package.json index 1ed9a37..8a96f4d 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "devDependencies": { "@types/big.js": "^6.2.2", "@types/bn.js": "^5.1.6", + "@types/bs58": "^4.0.4", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", diff --git a/src/locker-utils/index.ts b/src/locker-utils/index.ts index 29b367f..52c37fe 100644 --- a/src/locker-utils/index.ts +++ b/src/locker-utils/index.ts @@ -116,7 +116,7 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { ASSOCIATED_TOKEN_PROGRAM_ID ); - let remainingAccountsInfo; + let remainingAccountsInfo: OptionRemainingAccountsInfoData | null = null; let remainingAccounts: AccountMeta[] = []; if (tokenProgram == TOKEN_2022_PROGRAM_ID) { let inputTransferHookAccounts = @@ -196,8 +196,7 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { return escrow; } - - throw new Error; + throw error; } } @@ -227,7 +226,7 @@ export async function claimTokenV2(params: ClaimTokenParamsV2) { ASSOCIATED_TOKEN_PROGRAM_ID ); - let remainingAccountsInfo; + let remainingAccountsInfo: OptionRemainingAccountsInfoData | null = null; let remainingAccounts: AccountMeta[] | undefined = []; if (tokenProgram == TOKEN_2022_PROGRAM_ID) { let claimTransferHookAccounts = -- 2.45.2 From 7aa0d44f61683038d72e4ebc3de9edd3651e2a03 Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 6 Feb 2025 16:36:11 +0530 Subject: [PATCH 12/16] Use BN instead of Big in create lock --- package.json | 2 +- src/app/api/lock/route.ts | 7 ++-- src/locker-utils/index.ts | 77 ++------------------------------------ src/utils/extractTxInfo.ts | 22 +++++++++++ src/utils/verifyPayment.ts | 18 +-------- src/utils/verifyTweet.ts | 5 ++- 6 files changed, 35 insertions(+), 96 deletions(-) create mode 100644 src/utils/extractTxInfo.ts diff --git a/package.json b/package.json index 8a96f4d..2e18549 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "@coral-xyz/anchor": "^0.30.1", "@fal-ai/client": "^1.2.1", "@google/generative-ai": "^0.21.0", - "@solana/spl-token": "^0.3.8", + "@solana/spl-token": "^0.3.11", "@solana/web3.js": "^1.78.4", "big.js": "^6.2.2", "bn.js": "^5.2.0", diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts index ce4dc3c..ce1f981 100644 --- a/src/app/api/lock/route.ts +++ b/src/app/api/lock/route.ts @@ -1,15 +1,14 @@ import { NextRequest, NextResponse } from "next/server"; -import { Connection} from "@solana/web3.js"; -import { createRewardLock, extractInfo } from "../../../utils/create-lock"; -const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL!); +import { createRewardLock } from "../../../utils/create-lock"; +import { extractTxInfo } from "../../../utils/extractTxInfo"; export async function GET(req: NextRequest) { try { const { searchParams } = new URL(req.url); const signature = searchParams.get('signature') || '4HBtnoNUuMGpmbhD9cPiJtbxkhux31pfZs3HYud5eopAU69RaC4UbJsYdj83eafFxV6eH8pSaRgqELrwyjrWp7yz'; - const { authority, amount } = await extractInfo(signature); + const { authority, amount } = await extractTxInfo(signature); if (!authority || Number(amount) <= 0) { return NextResponse.json({ error: "Invalid transaction details" }, { status: 400 }); } diff --git a/src/locker-utils/index.ts b/src/locker-utils/index.ts index 52c37fe..58f102a 100644 --- a/src/locker-utils/index.ts +++ b/src/locker-utils/index.ts @@ -14,7 +14,6 @@ import { createAssociatedTokenAccountInstruction, getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID, - TOKEN_PROGRAM_ID, } from '@solana/spl-token'; import { AnchorProvider, @@ -39,12 +38,6 @@ assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL); -const ESCROW_USE_SPL_TOKEN = 0; - -const MEMO_PROGRAM = new web3.PublicKey( - "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" -); - export function createLockerProgram(wallet: Wallet): Program { const provider = new AnchorProvider(connection, wallet, { maxRetries: 3, @@ -80,8 +73,9 @@ export interface CreateVestingPlanParams { // V2 instructions export async function createVestingPlanV2(params: CreateVestingPlanParams) { let { - tokenMint, ownerKeypair, + tokenMint, + isAssertion, vestingStartTime, cliffTime, frequency, @@ -183,7 +177,7 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { return escrow; } catch (error) { if (error instanceof TransactionExpiredTimeoutError) { - console.error('Transaction confirmation delayed for',error.signature); + console.error('Transaction confirmation delayed for', error.signature); console.log('Confirming the transaction again...'); const confirmedTransaction = await connection.getTransaction(error.signature, { commitment: 'confirmed', @@ -191,7 +185,7 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { }); if(confirmedTransaction === null) { - console.error('Transaction failed for',error.signature); + console.error('Transaction failed for', error.signature); } return escrow; @@ -199,66 +193,3 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { throw error; } } - -export interface ClaimTokenParamsV2 { - isAssertion: boolean; - escrow: web3.PublicKey; - recipient: web3.Keypair; - maxAmount: BN; - recipientToken: web3.PublicKey; - tokenProgram: web3.PublicKey; -} - -export async function claimTokenV2(params: ClaimTokenParamsV2) { - let { isAssertion, escrow, recipient, maxAmount, recipientToken } = params; - const program = createLockerProgram(new Wallet(recipient)); - const escrowState = await program.account.vestingEscrow.fetch(escrow); - const tokenProgram = - escrowState.tokenProgramFlag == ESCROW_USE_SPL_TOKEN - ? TOKEN_PROGRAM_ID - : TOKEN_2022_PROGRAM_ID; - - const escrowToken = getAssociatedTokenAddressSync( - escrowState.tokenMint, - escrow, - true, - tokenProgram, - ASSOCIATED_TOKEN_PROGRAM_ID - ); - - let remainingAccountsInfo: OptionRemainingAccountsInfoData | null = null; - let remainingAccounts: AccountMeta[] | undefined = []; - if (tokenProgram == TOKEN_2022_PROGRAM_ID) { - let claimTransferHookAccounts = - await TokenExtensionUtil.getExtraAccountMetasForTransferHook( - program.provider.connection, - escrowState.tokenMint, - escrowToken, - recipientToken, - escrow, - TOKEN_2022_PROGRAM_ID - ); - - [remainingAccountsInfo, remainingAccounts] = new RemainingAccountsBuilder() - .addSlice( - RemainingAccountsType.TransferHookEscrow, - claimTransferHookAccounts - ) - .build(); - } - - const tx = await program.methods - .claimV2(maxAmount, remainingAccountsInfo) - .accounts({ - tokenProgram, - tokenMint: escrowState.tokenMint, - memoProgram: MEMO_PROGRAM, - escrow, - escrowToken, - recipient: recipient.publicKey, - recipientToken, - } as any) - .remainingAccounts(remainingAccounts ? remainingAccounts : []) - .signers([recipient]) - .rpc(); -} diff --git a/src/utils/extractTxInfo.ts b/src/utils/extractTxInfo.ts new file mode 100644 index 0000000..162c390 --- /dev/null +++ b/src/utils/extractTxInfo.ts @@ -0,0 +1,22 @@ +import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; +import { Connection } from "@solana/web3.js"; + +const connection = new Connection(process.env.NEXT_PUBLIC_SOLANA_RPC_URL!); + +export async function extractTxInfo(transactionSignature: string) { + const result = await connection.getParsedTransaction(transactionSignature, 'confirmed'); + if (!result) { + throw new Error('Transaction not found'); + } + + const transferInstruction = result.transaction.message.instructions.find( + (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) + ); + + if (!transferInstruction || !('parsed' in transferInstruction)) { + throw new Error('Transfer instruction not found'); + } + + const { info: { amount, authority } } = transferInstruction.parsed; + return { authority, amount }; + } \ No newline at end of file diff --git a/src/utils/verifyPayment.ts b/src/utils/verifyPayment.ts index 63dc2e2..efa87c4 100644 --- a/src/utils/verifyPayment.ts +++ b/src/utils/verifyPayment.ts @@ -3,6 +3,7 @@ import BN from 'bn.js'; import { Connection } from '@solana/web3.js'; import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; +import { extractTxInfo } from './extractTxInfo'; assert(process.env.NEXT_PUBLIC_SOLANA_RPC_URL, 'SOLANA_RPC_URL is required'); @@ -53,22 +54,7 @@ export async function verifyPayment( return false; } - const transaction = await connection.getParsedTransaction(transactionSignature, 'confirmed'); - if (!transaction) { - throw new Error('Transaction not found'); - } - - const transferInstruction = transaction.transaction.message.instructions.find( - (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) - ); - - if (!transferInstruction || !('parsed' in transferInstruction)) { - throw new Error('Transfer instruction not found'); - } - - const { parsed } = transferInstruction; - const { info } = parsed; - const { amount } = info; + const { amount } = await extractTxInfo(transactionSignature); const transactionAmount = new BN(amount); diff --git a/src/utils/verifyTweet.ts b/src/utils/verifyTweet.ts index 26c701d..d102944 100644 --- a/src/utils/verifyTweet.ts +++ b/src/utils/verifyTweet.ts @@ -1,7 +1,8 @@ import { DataSource, EntityTarget } from 'typeorm'; import { Tweet } from '../entity/Tweet'; -import { createRewardLock, extractInfo } from './create-lock'; +import { createRewardLock } from './create-lock'; +import { extractTxInfo } from './extractTxInfo'; export async function verifySignatureInTweet(transactionSignature: string): Promise { const paymentRepository = global.appDataSource.getRepository(global.entities.Payment); @@ -34,7 +35,7 @@ export async function processTweet(txSignature: string, memeUrl: string | null) try { if (isFourthUser) { - const { authority, amount } = await extractInfo(txSignature); + const { authority, amount } = await extractTxInfo(txSignature); if (!authority || Number(amount) <= 0) { return { error: "Invalid transaction details" } -- 2.45.2 From 33350b263e68cb8000f0a8f280dbd6f1ff59b936 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 16:37:05 +0530 Subject: [PATCH 13/16] Update BN round up logic --- src/app/page.tsx | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 8d7fe42..1b25ee5 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -62,18 +62,17 @@ const Page: React.FC = (): React.ReactElement => { } } - const roundUpBN = (bnValue: BN) : BN => { - // Divide by 10^6 - const decimalBN = bnValue.div(new BN (10 ** 6)); - // Conver to Big decimal - const bigValue = new Big(decimalBN.toString()) - // Ceil the result - const ceiledValue = bigValue.add(1); - // Convert back to BN - const ceiledBN = new BN(ceiledValue.toString()); - // Multiple by 10^6 - return ceiledBN.mul(new BN (10 ** 6)) - } + const roundUpBigNumber = (bnValue: BN): BN => { + const bigNumber = new Big(bnValue.toString()); + const bigNumberInUnits = bigNumber.div(new Big(10 ** 6)); + + const roundedUpValue = bigNumberInUnits.round(0, Big.roundUp); + + // Multiply by 10^6 to revert back to original scale + const scaledValue = roundedUpValue.mul(new Big(10 ** 6)); + + return new BN(scaledValue.toString()); + }; const handleFluxGeneration = (modelId: string, cost: BN) => { return async (prompt: string): Promise => { @@ -89,7 +88,7 @@ const Page: React.FC = (): React.ReactElement => { // Convert cost in USDC to MTM tokens using the price ratio const paymentResult = await processMTMPayment( publicKey, - roundUpBN(cost), + roundUpBigNumber(cost), type ) -- 2.45.2 From 5be2edfd43bc2892c8ba091e0df706eecc1b2e3c Mon Sep 17 00:00:00 2001 From: AdityaSalunkhe21 Date: Thu, 6 Feb 2025 16:43:44 +0530 Subject: [PATCH 14/16] Comment out create lock API --- src/app/api/lock/route.ts | 30 +++++++++++++++--------------- src/utils/extractTxInfo.ts | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/app/api/lock/route.ts b/src/app/api/lock/route.ts index ce1f981..c0d6e10 100644 --- a/src/app/api/lock/route.ts +++ b/src/app/api/lock/route.ts @@ -3,21 +3,21 @@ import { NextRequest, NextResponse } from "next/server"; import { createRewardLock } from "../../../utils/create-lock"; import { extractTxInfo } from "../../../utils/extractTxInfo"; -export async function GET(req: NextRequest) { - try { - const { searchParams } = new URL(req.url); - const signature = searchParams.get('signature') || '4HBtnoNUuMGpmbhD9cPiJtbxkhux31pfZs3HYud5eopAU69RaC4UbJsYdj83eafFxV6eH8pSaRgqELrwyjrWp7yz'; +// export async function GET(req: NextRequest) { +// try { +// const { searchParams } = new URL(req.url); +// const signature = searchParams.get('signature') - const { authority, amount } = await extractTxInfo(signature); - if (!authority || Number(amount) <= 0) { - return NextResponse.json({ error: "Invalid transaction details" }, { status: 400 }); - } +// const { authority, amount } = await extractTxInfo(signature); +// if (!authority || Number(amount) <= 0) { +// return NextResponse.json({ error: "Invalid transaction details" }, { status: 400 }); +// } - const escrow = await createRewardLock(authority, amount); - return NextResponse.json({ success: true, data: { escrow } }); +// const escrow = await createRewardLock(authority, amount); +// return NextResponse.json({ success: true, data: { escrow } }); - } catch (error) { - console.error('API route error:', error); - return NextResponse.json({ error: "Internal server error" }, { status: 500 }); - } -} +// } catch (error) { +// console.error('API route error:', error); +// return NextResponse.json({ error: "Internal server error" }, { status: 500 }); +// } +// } diff --git a/src/utils/extractTxInfo.ts b/src/utils/extractTxInfo.ts index 162c390..555eeef 100644 --- a/src/utils/extractTxInfo.ts +++ b/src/utils/extractTxInfo.ts @@ -19,4 +19,4 @@ export async function extractTxInfo(transactionSignature: string) { const { info: { amount, authority } } = transferInstruction.parsed; return { authority, amount }; - } \ No newline at end of file + } -- 2.45.2 From c923edf3c5c16791800e2fcc25b2308f6c4972d6 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 18:04:08 +0530 Subject: [PATCH 15/16] Store escrow in tweet table on successful lock creation --- src/entity/Tweet.ts | 6 +++++ src/locker-utils/index.ts | 1 + src/utils/create-lock.ts | 2 +- src/utils/verifyTweet.ts | 57 ++++++++++++++++++++++++--------------- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/entity/Tweet.ts b/src/entity/Tweet.ts index 888a9d9..c3f5a07 100644 --- a/src/entity/Tweet.ts +++ b/src/entity/Tweet.ts @@ -10,4 +10,10 @@ export class Tweet { @Column({ unique: true }) transactionSignature!: string; + + @Column({ type: 'boolean', nullable: true }) + isLockCreated!: boolean | null; + + @Column({ type: 'text', unique: true, nullable: true }) + lockEscrow!: string | null; } diff --git a/src/locker-utils/index.ts b/src/locker-utils/index.ts index 58f102a..40628c5 100644 --- a/src/locker-utils/index.ts +++ b/src/locker-utils/index.ts @@ -186,6 +186,7 @@ export async function createVestingPlanV2(params: CreateVestingPlanParams) { if(confirmedTransaction === null) { console.error('Transaction failed for', error.signature); + throw error; } return escrow; diff --git a/src/utils/create-lock.ts b/src/utils/create-lock.ts index ca67d16..727fa53 100644 --- a/src/utils/create-lock.ts +++ b/src/utils/create-lock.ts @@ -34,7 +34,7 @@ const provider = new anchor.AnchorProvider( anchor.setProvider(provider); -export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipientPubKey: anchor.web3.PublicKey, duration: BN, balance: BN): Promise { +export async function createLock(tokenLockerKeypair: anchor.web3.Keypair, recipientPubKey: anchor.web3.PublicKey, duration: BN, balance: BN): Promise { if (balance.eq(new BN(0))) { console.log('No balance available to create lock, skipping...'); diff --git a/src/utils/verifyTweet.ts b/src/utils/verifyTweet.ts index d102944..f20a274 100644 --- a/src/utils/verifyTweet.ts +++ b/src/utils/verifyTweet.ts @@ -23,34 +23,47 @@ export async function verifySignatureInTweet(transactionSignature: string): Prom } export async function processTweet(txSignature: string, memeUrl: string | null) { - return await (global.appDataSource as DataSource).transaction(async (transactionalEntityManager) => { - const tweetRepository = transactionalEntityManager.getRepository(global.entities.Tweet as EntityTarget); + const tweetRepository = (global.appDataSource as DataSource).getRepository( + global.entities.Tweet as EntityTarget + ); - const tweet = await tweetRepository.save({ - transactionSignature: txSignature, - url: memeUrl, - }); + const tweet = await tweetRepository.save({ + transactionSignature: txSignature, + url: memeUrl, + }); - const isFourthUser = tweet.id % 4 === 0; + const isFourthUser = tweet.id % 4 === 0; - try { - if (isFourthUser) { - const { authority, amount } = await extractTxInfo(txSignature); + try { + if (isFourthUser) { + const { authority, amount } = await extractTxInfo(txSignature); - if (!authority || Number(amount) <= 0) { - return { error: "Invalid transaction details" } - } - - const escrow = await createRewardLock(authority, amount); - - return { success: true, data: { escrow } }; + if (!authority || Number(amount) <= 0) { + return { error: "Invalid transaction details" }; } - return { success: true, message: 'Tweet verified' } - } catch (error) { - console.error('Error locking tokens.'); + const escrow = await createRewardLock(authority, amount); - throw new Error("Transaction failed."); + if (!escrow) { + throw new Error("Lock not created"); + } + + await tweetRepository.update(tweet.id, { + isLockCreated: true, + lockEscrow: escrow.toString() + }); + + return { success: true, data: { escrow } }; } - }); + + return { success: true, message: 'Tweet verified' }; + } catch (error) { + await tweetRepository.update(tweet.id, { + isLockCreated: false, + }); + + console.error('Error locking tokens.'); + + throw new Error("Transaction failed."); + } } -- 2.45.2 From f53fb1c312e4f1f3ca3206f1f36e5ee54f332733 Mon Sep 17 00:00:00 2001 From: Adw8 Date: Thu, 6 Feb 2025 18:13:41 +0530 Subject: [PATCH 16/16] Add log --- src/utils/verifyTweet.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/verifyTweet.ts b/src/utils/verifyTweet.ts index f20a274..16f27e3 100644 --- a/src/utils/verifyTweet.ts +++ b/src/utils/verifyTweet.ts @@ -62,7 +62,7 @@ export async function processTweet(txSignature: string, memeUrl: string | null) isLockCreated: false, }); - console.error('Error locking tokens.'); + console.error('Error locking tokens: ', error); throw new Error("Transaction failed."); } -- 2.45.2