diff --git a/package-lock.json b/package-lock.json index b3fb95a..c2d9626 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pezkuwi-telegram-miniapp", - "version": "1.0.107", + "version": "1.0.169", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pezkuwi-telegram-miniapp", - "version": "1.0.107", + "version": "1.0.169", "license": "MIT", "dependencies": { "@pezkuwi/api": "^16.5.36", @@ -14,6 +14,8 @@ "@pezkuwi/util-crypto": "^14.0.25", "@supabase/supabase-js": "^2.93.1", "@tanstack/react-query": "^5.56.2", + "@ton/crypto": "^3.3.0", + "@ton/ton": "^16.2.2", "@types/qrcode": "^1.5.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -36,6 +38,7 @@ "@vitejs/plugin-react-swc": "^3.5.0", "@vitest/coverage-v8": "^4.0.18", "autoprefixer": "^10.4.20", + "bip39": "^3.1.0", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", @@ -48,6 +51,7 @@ "postcss": "^8.4.47", "prettier": "^3.8.1", "tailwindcss": "^3.4.11", + "tronweb": "^6.2.0", "typescript": "^5.5.3", "vite": "^5.4.1", "vitest": "^4.0.18" @@ -70,6 +74,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "dev": true, + "license": "MIT" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -2331,6 +2342,94 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/@scure/bip32": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.4.0.tgz", + "integrity": "sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.4.0", + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39/node_modules/@scure/base": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz", + "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -2752,6 +2851,60 @@ } } }, + "node_modules/@ton/core": { + "version": "0.63.0", + "resolved": "https://registry.npmjs.org/@ton/core/-/core-0.63.0.tgz", + "integrity": "sha512-uBc0WQNYVzjAwPvIazf0Ryhpv4nJd4dKIuHoj766gUdwe8sVzGM+TxKKKJETL70hh/mxACyUlR4tAwN0LWDNow==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@ton/crypto": ">=3.2.0" + } + }, + "node_modules/@ton/crypto": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@ton/crypto/-/crypto-3.3.0.tgz", + "integrity": "sha512-/A6CYGgA/H36OZ9BbTaGerKtzWp50rg67ZCH2oIjV1NcrBaCK9Z343M+CxedvM7Haf3f/Ee9EhxyeTp0GKMUpA==", + "license": "MIT", + "dependencies": { + "@ton/crypto-primitives": "2.1.0", + "jssha": "3.2.0", + "tweetnacl": "1.0.3" + } + }, + "node_modules/@ton/crypto-primitives": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@ton/crypto-primitives/-/crypto-primitives-2.1.0.tgz", + "integrity": "sha512-PQesoyPgqyI6vzYtCXw4/ZzevePc4VGcJtFwf08v10OevVJHVfW238KBdpj1kEDQkxWLeuNHEpTECNFKnP6tow==", + "license": "MIT", + "dependencies": { + "jssha": "3.2.0" + } + }, + "node_modules/@ton/ton": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/@ton/ton/-/ton-16.2.2.tgz", + "integrity": "sha512-yEOw4IW3gpRZxJAcILMI4dQ1d5/eAAbD2VU/Iwc6z7f2jt1mLDWVED8yn2vLNucQfZr+1eaqYHLztYVFZ7PKmw==", + "license": "MIT", + "dependencies": { + "axios": "^1.6.7", + "dataloader": "^2.0.0", + "zod": "^3.21.4" + }, + "peerDependencies": { + "@ton/core": ">=0.63.0 <1.0.0", + "@ton/crypto": ">=3.2.0" + } + }, + "node_modules/@ton/ton/node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -3246,6 +3399,13 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true, + "license": "MIT" + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -3562,6 +3722,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.24", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", @@ -3614,6 +3780,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axios": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", + "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.4", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3661,6 +3838,16 @@ "require-from-string": "^2.0.2" } }, + "node_modules/bignumber.js": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", + "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -3673,6 +3860,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bip39": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz", + "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==", + "dev": true, + "license": "ISC", + "dependencies": { + "@noble/hashes": "^1.2.0" + } + }, "node_modules/bn.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", @@ -4246,6 +4443,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", @@ -4530,6 +4739,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dataloader": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-2.2.3.tgz", + "integrity": "sha512-y2krtASINtPFS1rSDjacrFgn1dcUuoREVabwlOGOe4SdxenREqwjwjElAdwvbGM7kgZz9a3KVicWR7vcz8rnzA==", + "license": "MIT" + }, "node_modules/date-fns": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", @@ -4614,6 +4829,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", @@ -4915,7 +5139,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -5377,6 +5600,146 @@ "node": ">=0.10.0" } }, + "node_modules/ethereum-cryptography": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz", + "integrity": "sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/curves": "1.4.2", + "@noble/hashes": "1.4.0", + "@scure/bip32": "1.4.0", + "@scure/bip39": "1.3.0" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/curves": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", + "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethereum-cryptography/node_modules/@noble/hashes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.5.tgz", + "integrity": "sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", @@ -5562,6 +5925,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -5577,6 +5960,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -5795,6 +6194,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/google-protobuf": { + "version": "3.21.4", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz", + "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==", + "dev": true, + "license": "(BSD-3-Clause AND Apache-2.0)" + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -6766,6 +7172,15 @@ "node": ">=6" } }, + "node_modules/jssha": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz", + "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q==", + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -7056,6 +7471,27 @@ "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", "license": "MIT" }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", @@ -8130,6 +8566,12 @@ "node": ">= 8" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/public-encrypt": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", @@ -8344,6 +8786,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -9532,6 +9981,57 @@ "node": ">=20" } }, + "node_modules/tronweb": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tronweb/-/tronweb-6.2.0.tgz", + "integrity": "sha512-09kyW6mqiFuSYXkR35ndxCeNF5rW1O18hKAClCLtVHP2xBFPYSGx3lDYC2hRKcuLiq6iLPxOVCrhzoKNGlFuQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "7.26.10", + "axios": "1.12.2", + "bignumber.js": "9.1.2", + "ethereum-cryptography": "2.2.1", + "ethers": "6.13.5", + "eventemitter3": "5.0.1", + "google-protobuf": "3.21.4", + "semver": "7.7.1", + "validator": "13.15.23" + } + }, + "node_modules/tronweb/node_modules/@babel/runtime": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/tronweb/node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tronweb/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", @@ -9563,6 +10063,12 @@ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", "license": "MIT" }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==", + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -9771,6 +10277,16 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/validator": { + "version": "13.15.23", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", + "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", diff --git a/package.json b/package.json index f3a448d..abfb1c4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pezkuwi-telegram-miniapp", - "version": "1.0.169", + "version": "1.0.170", "type": "module", "description": "Pezkuwichain Telegram Mini App - Forum, Announcements, Rewards", "author": "Pezkuwichain Team", @@ -42,6 +42,8 @@ "@pezkuwi/util-crypto": "^14.0.25", "@supabase/supabase-js": "^2.93.1", "@tanstack/react-query": "^5.56.2", + "@ton/crypto": "^3.3.0", + "@ton/ton": "^16.2.2", "@types/qrcode": "^1.5.6", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -64,6 +66,7 @@ "@vitejs/plugin-react-swc": "^3.5.0", "@vitest/coverage-v8": "^4.0.18", "autoprefixer": "^10.4.20", + "bip39": "^3.1.0", "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", "eslint-plugin-prettier": "^5.5.5", diff --git a/src/components/wallet/DepositUSDTModal.tsx b/src/components/wallet/DepositUSDTModal.tsx index b488457..1216a4b 100644 --- a/src/components/wallet/DepositUSDTModal.tsx +++ b/src/components/wallet/DepositUSDTModal.tsx @@ -1,6 +1,6 @@ /** * Deposit USDT Modal - * Allows users to deposit USDT (TRC20 or Polkadot) to get wUSDT on Asset Hub + * Supports TON, Polkadot (recommended) and TRC20 (with fee warning) */ import { useState, useEffect } from 'react'; @@ -13,43 +13,57 @@ import { Loader2, History, Plus, + AlertTriangle, } from 'lucide-react'; import { useTelegram } from '@/hooks/useTelegram'; import { supabase } from '@/lib/supabase'; -type Network = 'trc20' | 'polkadot'; +type Network = 'ton' | 'polkadot' | 'trc20'; interface NetworkInfo { id: Network; name: string; description: string; - address: string; - explorer: string; icon: string; - minAmount: number; - confirmations: number; + recommended: boolean; + fee: number; + feeWarning?: string; + explorer: string; + minDeposit: number; } const NETWORKS: NetworkInfo[] = [ { - id: 'trc20', - name: 'TRC20 (TRON)', - description: 'USDT li ser tora TRON', - address: import.meta.env.VITE_DEPOSIT_TRON_ADDRESS || '', - explorer: 'https://tronscan.org/#/transaction/', - icon: '🔴', - minAmount: 10, - confirmations: 20, + id: 'ton', + name: 'TON', + description: 'Telegram Wallet', + icon: '💎', + recommended: true, + fee: 0.05, + explorer: 'https://tonviewer.com/transaction/', + minDeposit: 10, }, { id: 'polkadot', - name: 'Polkadot Asset Hub', - description: 'USDT li ser Polkadot', - address: import.meta.env.VITE_DEPOSIT_POLKADOT_ADDRESS || '', - explorer: 'https://assethub-polkadot.subscan.io/extrinsic/', + name: 'Polkadot', + description: 'Asset Hub', icon: '⚪', - minAmount: 10, - confirmations: 1, + recommended: true, + fee: 0.05, + explorer: 'https://assethub-polkadot.subscan.io/extrinsic/', + minDeposit: 10, + }, + { + id: 'trc20', + name: 'TRC20', + description: 'TRON Network', + icon: '🔴', + recommended: false, + fee: 3, + feeWarning: + 'TRC20 ağ masrafı yaklaşık $3 civarındadır. 1000 USDT altındaki gönderimlerde verimli değildir. TON veya Polkadot ağını öneriyoruz.', + explorer: 'https://tronscan.org/#/transaction/', + minDeposit: 10, }, ]; @@ -71,18 +85,35 @@ interface Props { export function DepositUSDTModal({ isOpen, onClose }: Props) { const { hapticImpact, showAlert } = useTelegram(); - const [selectedNetwork, setSelectedNetwork] = useState('trc20'); + const [selectedNetwork, setSelectedNetwork] = useState('ton'); const [depositCode, setDepositCode] = useState(''); + const [depositAddress, setDepositAddress] = useState(''); const [copied, setCopied] = useState<'address' | 'memo' | null>(null); const [deposits, setDeposits] = useState([]); const [showHistory, setShowHistory] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [trc20Accepted, setTrc20Accepted] = useState(false); const network = NETWORKS.find((n) => n.id === selectedNetwork) || NETWORKS[0]; - // Fetch user's deposit code via edge function + // Get deposit address based on network + const getNetworkAddress = (networkId: Network): string => { + switch (networkId) { + case 'ton': + return import.meta.env.VITE_DEPOSIT_TON_ADDRESS || ''; + case 'polkadot': + return import.meta.env.VITE_DEPOSIT_POLKADOT_ADDRESS || ''; + case 'trc20': + // TRC20 uses HD wallet - address comes from backend + return depositAddress; + default: + return ''; + } + }; + + // Fetch user's deposit code and TRC20 address useEffect(() => { - const fetchDepositCode = async () => { + const fetchDepositInfo = async () => { if (!isOpen) return; const initData = window.Telegram?.WebApp?.initData; @@ -93,25 +124,26 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { setIsLoading(true); try { - const { data, error } = await supabase.functions.invoke('get-deposit-code', { + const { data, error } = await supabase.functions.invoke('get-deposit-info', { body: { initData }, }); if (error) { - console.error('Error fetching deposit code:', error); + console.error('Error fetching deposit info:', error); setDepositCode('---'); - } else if (data?.code) { - setDepositCode(data.code); + } else { + if (data?.code) setDepositCode(data.code); + if (data?.trc20Address) setDepositAddress(data.trc20Address); } } catch (err) { - console.error('Error fetching deposit code:', err); + console.error('Error fetching deposit info:', err); setDepositCode('---'); } finally { setIsLoading(false); } }; - fetchDepositCode(); + fetchDepositInfo(); }, [isOpen]); // Fetch deposits history @@ -179,6 +211,9 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { } }; + const currentAddress = getNetworkAddress(selectedNetwork); + const showTrc20Warning = selectedNetwork === 'trc20' && !trc20Accepted; + if (!isOpen) return null; return ( @@ -223,7 +258,13 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) {
- {deposit.network === 'trc20' ? '🔴' : '⚪'} + + {deposit.network === 'ton' + ? '💎' + : deposit.network === 'polkadot' + ? '⚪' + : '🔴'} + {deposit.amount} USDT

@@ -260,128 +301,210 @@ export function DepositUSDTModal({ isOpen, onClose }: Props) { {/* Network Selection */}

-
+
{NETWORKS.map((net) => ( ))}
- {/* Important Notice */} -
-
- -
-

Girîng!

-
    -
  • - Kêmtirîn: {network.minAmount} USDT -
  • -
  • Memo/Not qada de koda xwe binivîse
  • -
  • Tenê USDT bişîne, tokenên din winda dibin
  • -
+ {/* TRC20 Warning */} + {showTrc20Warning && ( +
+
+ +
+

Dikkkat!

+

{network.feeWarning}

+

+ Mînak: 10 USDT bişîne → 7 wUSDT werbigire ($3 masraf) +

+ +
-
+ )} - {/* Deposit Address */} -
- {/* Address */} -
- -
- - {network.address || 'Amade nîne'} - - -
-
- - {/* Memo/Reference */} -
- -
- {isLoading ? ( -
- + {/* Deposit Info - Show only when TRC20 is accepted or other networks */} + {(selectedNetwork !== 'trc20' || trc20Accepted) && ( + <> + {/* Important Notice */} +
+
+ +
+

Girîng!

+
    +
  • + Kêmtirîn: {network.minDeposit} USDT +
  • + {selectedNetwork !== 'trc20' && ( +
  • Memo/Comment qada de koda xwe binivîse
  • + )} +
  • Tenê USDT bişîne, tokenên din winda dibin
  • + {selectedNetwork === 'trc20' && ( +
  • + $3 masraf dê ji mîqdara we bê kêmkirin +
  • + )} +
- ) : ( - - {depositCode || '---'} - - )} - +
-

- ⚠️ Vê kodê di memo/not de binivîse, wekî din depoya te nayê nas kirin! + + {/* Deposit Address */} +

+ {/* Address */} +
+ +
+ {isLoading && selectedNetwork === 'trc20' ? ( +
+ +
+ ) : ( + + {currentAddress || 'Amade nîne'} + + )} + +
+
+ + {/* Memo/Reference - Not needed for TRC20 (unique address) */} + {selectedNetwork !== 'trc20' && ( +
+ +
+ {isLoading ? ( +
+ +
+ ) : ( + + {depositCode || '---'} + + )} + +
+

+ ⚠️ Vê kodê di memo de binivîse, wekî din depoya te nayê nas kirin! +

+
+ )} + + {selectedNetwork === 'trc20' && ( +

+ ✅ Ev navnîşan tenê ya te ye. Memo ne pêwîst e. +

+ )} +
+ + {/* Steps */} +
+

Çawa Depo Bikim?

+
    +
  1. + 1. + Navnîşan kopî bike +
  2. + {selectedNetwork !== 'trc20' && ( +
  3. + 2. + Memo kodê kopî bike +
  4. + )} +
  5. + + {selectedNetwork === 'trc20' ? '2' : '3'}. + + + {selectedNetwork === 'ton' + ? 'Telegram Wallet an cîzdana xwe veke' + : selectedNetwork === 'polkadot' + ? 'Polkadot cîzdana xwe veke' + : 'TronLink an cîzdana xwe veke'} + +
  6. +
  7. + + {selectedNetwork === 'trc20' ? '3' : '4'}. + + USDT bişîne navnîşana jorîn +
  8. +
  9. + + {selectedNetwork === 'trc20' ? '4' : '5'}. + + wUSDT dê di nav çend hûrdeman de li hesabê te be +
  10. +
+
+ + {/* Processing Time */} +

+ Dema pêvajoyê: ~1-5 hûrdem

-
-
- - {/* Steps */} -
-

Çawa Depo Bikim?

-
    -
  1. - 1. - Navnîşan û memo kopî bike -
  2. -
  3. - 2. - Di cîzdana xwe de ({network.name}) USDT bişîne navnîşana jorîn -
  4. -
  5. - 3. - - MEMO qada de koda xwe binivîse! - -
  6. -
  7. - 4. - Piştî {network.confirmations} pejirandinê, wUSDT dê li Asset Hub be -
  8. -
-
- - {/* Processing Time */} -

- Dema pêvajoyê: ~5-30 hûrdem li gorî torê -

+ + )}
)}
diff --git a/src/version.json b/src/version.json index 9a143b5..f374fb6 100644 --- a/src/version.json +++ b/src/version.json @@ -1,5 +1,5 @@ { - "version": "1.0.169", - "buildTime": "2026-02-07T22:14:22.046Z", - "buildNumber": 1770502462047 + "version": "1.0.170", + "buildTime": "2026-02-07T23:00:20.055Z", + "buildNumber": 1770505220056 } diff --git a/supabase/functions/get-deposit-info/index.ts b/supabase/functions/get-deposit-info/index.ts new file mode 100644 index 0000000..392208c --- /dev/null +++ b/supabase/functions/get-deposit-info/index.ts @@ -0,0 +1,290 @@ +/** + * Get Deposit Info - Returns user's deposit code and TRC20 HD wallet address + */ + +import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'; +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; +import { createHmac } from 'https://deno.land/std@0.177.0/node/crypto.ts'; +import { HDKey } from 'https://esm.sh/@scure/bip32@1.3.2'; +import * as bip39 from 'https://esm.sh/@scure/bip39@1.2.1'; +import { wordlist } from 'https://esm.sh/@scure/bip39@1.2.1/wordlists/english'; +import { keccak_256 } from 'https://esm.sh/@noble/hashes@1.3.2/sha3'; +import { sha256 } from 'https://esm.sh/@noble/hashes@1.3.2/sha256'; +import { bytesToHex, hexToBytes } from 'https://esm.sh/@noble/hashes@1.3.2/utils'; +import { secp256k1 } from 'https://esm.sh/@noble/curves@1.2.0/secp256k1'; + +const ALLOWED_ORIGIN = 'https://telegram.pezkuwichain.io'; +const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'; + +function getCorsHeaders(): Record { + return { + 'Access-Control-Allow-Origin': ALLOWED_ORIGIN, + 'Access-Control-Allow-Headers': + 'authorization, x-client-info, apikey, content-type, x-supabase-client-platform', + 'Access-Control-Allow-Methods': 'POST, OPTIONS', + }; +} + +interface TelegramUser { + id: number; + first_name: string; +} + +function validateInitData(initData: string, botToken: string): TelegramUser | null { + try { + const params = new URLSearchParams(initData); + const hash = params.get('hash'); + if (!hash) return null; + + params.delete('hash'); + + const sortedParams = Array.from(params.entries()) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([key, value]) => `${key}=${value}`) + .join('\n'); + + const secretKey = createHmac('sha256', 'WebAppData').update(botToken).digest(); + const calculatedHash = createHmac('sha256', secretKey).update(sortedParams).digest('hex'); + + if (calculatedHash !== hash) return null; + + const authDate = parseInt(params.get('auth_date') || '0'); + const now = Math.floor(Date.now() / 1000); + if (now - authDate > 86400) return null; + + const userStr = params.get('user'); + if (!userStr) return null; + + return JSON.parse(userStr) as TelegramUser; + } catch { + return null; + } +} + +function generateDepositCode(): string { + const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; + let result = 'PEZ-'; + for (let i = 0; i < 8; i++) { + result += chars.charAt(Math.floor(Math.random() * chars.length)); + } + return result; +} + +// Base58Check encode +function base58CheckEncode(payload: Uint8Array): string { + // Double SHA256 for checksum + const hash1 = sha256(payload); + const hash2 = sha256(hash1); + const checksum = hash2.slice(0, 4); + + // Combine payload and checksum + const data = new Uint8Array(payload.length + 4); + data.set(payload); + data.set(checksum, payload.length); + + // Count leading zeros + let zeros = 0; + for (let i = 0; i < data.length && data[i] === 0; i++) { + zeros++; + } + + // Convert to base58 + let num = BigInt('0x' + bytesToHex(data)); + let result = ''; + + while (num > 0n) { + const remainder = Number(num % 58n); + result = BASE58_ALPHABET[remainder] + result; + num = num / 58n; + } + + // Add leading '1's for zeros + return '1'.repeat(zeros) + result; +} + +// Derive TRC20 address from HD wallet +function deriveTronAddress(mnemonic: string, index: number): string { + try { + // Convert mnemonic to seed + const seed = bip39.mnemonicToSeedSync(mnemonic, ''); + + // Create HD key from seed + const hdKey = HDKey.fromMasterSeed(seed); + + // Derive path: m/44'/195'/0'/0/{index} + // 195 is TRON's coin type + const derivedKey = hdKey.derive(`m/44'/195'/0'/0/${index}`); + + if (!derivedKey.privateKey) { + throw new Error('Failed to derive private key'); + } + + // Get uncompressed public key + const publicKey = secp256k1.getPublicKey(derivedKey.privateKey, false); + + // Skip the 0x04 prefix and hash with keccak256 + const publicKeyWithoutPrefix = publicKey.slice(1); + const hash = keccak_256(publicKeyWithoutPrefix); + + // Take last 20 bytes for address + const addressBytes = hash.slice(-20); + + // Add TRON mainnet prefix (0x41) + const addressWithPrefix = new Uint8Array(21); + addressWithPrefix[0] = 0x41; + addressWithPrefix.set(addressBytes, 1); + + // Base58Check encode + return base58CheckEncode(addressWithPrefix); + } catch (err) { + console.error('Error deriving TRON address:', err); + throw err; + } +} + +serve(async (req) => { + const corsHeaders = getCorsHeaders(); + + if (req.method === 'OPTIONS') { + return new Response('ok', { headers: corsHeaders }); + } + + try { + const body = await req.json(); + const { initData } = body; + + if (!initData) { + return new Response(JSON.stringify({ error: 'Missing initData' }), { + status: 400, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + }); + } + + const supabaseUrl = Deno.env.get('SUPABASE_URL')!; + const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; + const botToken = Deno.env.get('TELEGRAM_BOT_TOKEN'); + const tronHdMnemonic = Deno.env.get('DEPOSIT_TRON_HD_MNEMONIC'); + + if (!botToken) { + return new Response(JSON.stringify({ error: 'Server configuration error' }), { + status: 500, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + }); + } + + const telegramUser = validateInitData(initData, botToken); + if (!telegramUser) { + return new Response(JSON.stringify({ error: 'Invalid Telegram data' }), { + status: 401, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + }); + } + + const supabase = createClient(supabaseUrl, supabaseServiceKey, { + auth: { autoRefreshToken: false, persistSession: false }, + }); + + // Get or create user + let userId: string; + let depositIndex: number; + + const { data: existingUser } = await supabase + .from('tg_users') + .select('id, deposit_index') + .eq('telegram_id', telegramUser.id) + .single(); + + if (existingUser) { + userId = existingUser.id; + depositIndex = existingUser.deposit_index ?? -1; + + // Ensure deposit_index exists + if (depositIndex < 0) { + // Get next available index + const { data: maxIndexData } = await supabase + .from('tg_users') + .select('deposit_index') + .not('deposit_index', 'is', null) + .order('deposit_index', { ascending: false }) + .limit(1) + .single(); + + depositIndex = (maxIndexData?.deposit_index ?? -1) + 1; + + await supabase.from('tg_users').update({ deposit_index: depositIndex }).eq('id', userId); + } + } else { + // Get next available index + const { data: maxIndexData } = await supabase + .from('tg_users') + .select('deposit_index') + .not('deposit_index', 'is', null) + .order('deposit_index', { ascending: false }) + .limit(1) + .single(); + + depositIndex = (maxIndexData?.deposit_index ?? -1) + 1; + + const { data: newUser, error: createError } = await supabase + .from('tg_users') + .insert({ + telegram_id: telegramUser.id, + first_name: telegramUser.first_name, + deposit_index: depositIndex, + }) + .select('id') + .single(); + + if (createError || !newUser) { + return new Response(JSON.stringify({ error: 'Failed to create user' }), { + status: 500, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + }); + } + userId = newUser.id; + } + + // Get or create deposit code + let depositCode: string; + const { data: existingCode } = await supabase + .from('tg_user_deposit_codes') + .select('code') + .eq('user_id', userId) + .single(); + + if (existingCode) { + depositCode = existingCode.code; + } else { + depositCode = generateDepositCode(); + await supabase.from('tg_user_deposit_codes').insert({ + user_id: userId, + code: depositCode, + }); + } + + // Generate TRC20 address from HD wallet + let trc20Address = ''; + if (tronHdMnemonic && depositIndex >= 0) { + try { + trc20Address = deriveTronAddress(tronHdMnemonic, depositIndex); + } catch (err) { + console.error('Error deriving TRC20 address:', err); + } + } + + return new Response( + JSON.stringify({ + code: depositCode, + trc20Address, + depositIndex, + }), + { headers: { ...corsHeaders, 'Content-Type': 'application/json' } } + ); + } catch (err) { + console.error('Get deposit info error:', err); + return new Response(JSON.stringify({ error: 'Internal server error' }), { + status: 500, + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + }); + } +}); diff --git a/supabase/functions/process-deposits/index.ts b/supabase/functions/process-deposits/index.ts new file mode 100644 index 0000000..ef2db25 --- /dev/null +++ b/supabase/functions/process-deposits/index.ts @@ -0,0 +1,201 @@ +/** + * Process Deposits - Cron job to check for incoming USDT deposits + * Checks TRC20 (TRON) and Polkadot Asset Hub for incoming transfers + * Then transfers wUSDT to user's Asset Hub address + */ + +import { serve } from 'https://deno.land/std@0.177.0/http/server.ts'; +import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; + +const TRON_API = 'https://api.trongrid.io'; +const USDT_TRC20_CONTRACT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t'; // USDT TRC20 contract + +interface TRC20Transfer { + transaction_id: string; + from: string; + to: string; + value: string; + block_timestamp: number; +} + +interface DepositCode { + code: string; + user_id: string; + user_address: string; +} + +serve(async (req) => { + // Verify this is called by Supabase cron or with secret + const authHeader = req.headers.get('Authorization'); + const cronSecret = Deno.env.get('CRON_SECRET'); + + if (cronSecret && authHeader !== `Bearer ${cronSecret}`) { + // Also allow service role + const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY'); + if (authHeader !== `Bearer ${supabaseServiceKey}`) { + return new Response(JSON.stringify({ error: 'Unauthorized' }), { + status: 401, + headers: { 'Content-Type': 'application/json' }, + }); + } + } + + const supabaseUrl = Deno.env.get('SUPABASE_URL')!; + const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; + const tronAddress = Deno.env.get('DEPOSIT_TRON_ADDRESS'); + const polkadotAddress = Deno.env.get('DEPOSIT_POLKADOT_ADDRESS'); + + const supabase = createClient(supabaseUrl, supabaseServiceKey, { + auth: { autoRefreshToken: false, persistSession: false }, + }); + + const results = { + trc20: { checked: 0, found: 0, processed: 0, errors: [] as string[] }, + polkadot: { checked: 0, found: 0, processed: 0, errors: [] as string[] }, + }; + + // Get all deposit codes with user addresses + const { data: depositCodes } = await supabase.from('tg_user_deposit_codes').select(` + code, + user_id, + tg_users!inner(wallet_address) + `); + + const codeMap = new Map(); + if (depositCodes) { + for (const dc of depositCodes) { + const userAddress = (dc as any).tg_users?.wallet_address; + if (userAddress) { + codeMap.set(dc.code, { userId: dc.user_id, walletAddress: userAddress }); + } + } + } + + // ==================== TRC20 DEPOSITS ==================== + if (tronAddress) { + try { + // Get recent TRC20 transfers to our deposit address + const response = await fetch( + `${TRON_API}/v1/accounts/${tronAddress}/transactions/trc20?limit=50&contract_address=${USDT_TRC20_CONTRACT}`, + { + headers: { 'TRON-PRO-API-KEY': Deno.env.get('TRONGRID_API_KEY') || '' }, + } + ); + + if (response.ok) { + const data = await response.json(); + const transfers = (data.data || []) as TRC20Transfer[]; + results.trc20.checked = transfers.length; + + for (const tx of transfers) { + // Only incoming transfers + if (tx.to.toLowerCase() !== tronAddress.toLowerCase()) continue; + + // Check if already processed + const { data: existing } = await supabase + .from('tg_deposits') + .select('id') + .eq('tx_hash', tx.transaction_id) + .single(); + + if (existing) continue; + + results.trc20.found++; + + // USDT has 6 decimals + const amount = parseInt(tx.value) / 1e6; + + // For TRC20, we need to check the memo in the transaction + // Unfortunately, TRC20 transfers don't have memo field directly + // We'll need to match by amount or create pending deposits that admin confirms + + // Create pending deposit for manual review or amount matching + const { error: insertError } = await supabase.from('tg_deposits').insert({ + user_id: null, // Will be matched later + network: 'trc20', + amount, + tx_hash: tx.transaction_id, + status: 'confirming', + created_at: new Date(tx.block_timestamp).toISOString(), + }); + + if (insertError) { + results.trc20.errors.push(`Insert error: ${insertError.message}`); + } else { + results.trc20.processed++; + } + } + } + } catch (err) { + results.trc20.errors.push(`TRC20 error: ${err}`); + } + } + + // ==================== POLKADOT DEPOSITS ==================== + if (polkadotAddress) { + try { + // Use Subscan API for Polkadot Asset Hub + const subscanResponse = await fetch( + 'https://assethub-polkadot.api.subscan.io/api/v2/scan/transfers', + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-API-Key': Deno.env.get('SUBSCAN_API_KEY') || '', + }, + body: JSON.stringify({ + address: polkadotAddress, + direction: 'received', + row: 50, + page: 0, + asset_symbol: 'USDT', + }), + } + ); + + if (subscanResponse.ok) { + const data = await subscanResponse.json(); + const transfers = data.data?.transfers || []; + results.polkadot.checked = transfers.length; + + for (const tx of transfers) { + // Check if already processed + const { data: existing } = await supabase + .from('tg_deposits') + .select('id') + .eq('tx_hash', tx.hash) + .single(); + + if (existing) continue; + + results.polkadot.found++; + + // Polkadot USDT has 6 decimals + const amount = parseFloat(tx.amount) / 1e6; + + // Create pending deposit + const { error: insertError } = await supabase.from('tg_deposits').insert({ + user_id: null, + network: 'polkadot', + amount, + tx_hash: tx.hash, + status: 'confirming', + created_at: new Date(tx.block_timestamp * 1000).toISOString(), + }); + + if (insertError) { + results.polkadot.errors.push(`Insert error: ${insertError.message}`); + } else { + results.polkadot.processed++; + } + } + } + } catch (err) { + results.polkadot.errors.push(`Polkadot error: ${err}`); + } + } + + return new Response(JSON.stringify({ success: true, results }), { + headers: { 'Content-Type': 'application/json' }, + }); +}); diff --git a/supabase/migrations/20260208_add_deposit_index.sql b/supabase/migrations/20260208_add_deposit_index.sql new file mode 100644 index 0000000..e1b608c --- /dev/null +++ b/supabase/migrations/20260208_add_deposit_index.sql @@ -0,0 +1,16 @@ +-- Add deposit_index column for TRC20 HD wallet derivation +ALTER TABLE tg_users ADD COLUMN IF NOT EXISTS deposit_index INTEGER UNIQUE; + +-- Create index for faster lookups +CREATE INDEX IF NOT EXISTS idx_users_deposit_index ON tg_users(deposit_index); + +-- Assign deposit_index to existing users +WITH numbered_users AS ( + SELECT id, ROW_NUMBER() OVER (ORDER BY created_at) - 1 as idx + FROM tg_users + WHERE deposit_index IS NULL +) +UPDATE tg_users +SET deposit_index = numbered_users.idx +FROM numbered_users +WHERE tg_users.id = numbered_users.id;