From 2d260dd7f3be18ae31b4ab2dc82e05f5c9603b6d Mon Sep 17 00:00:00 2001 From: toninorair Date: Fri, 7 Jun 2024 16:36:08 -0400 Subject: [PATCH 01/10] first prototype --- foundry.toml | 3 +- package-lock.json | 442 ++++++++++++++++++++++++++++++++++++++++++++-- script/Foo.s.sol | 17 -- src/Foo.sol | 9 - src/WM.sol | 242 +++++++++++++++++++++++++ src/Wrapper.sol | 37 ++++ test/Foo.t.sol | 41 ----- 7 files changed, 705 insertions(+), 86 deletions(-) delete mode 100644 script/Foo.s.sol delete mode 100644 src/Foo.sol create mode 100644 src/WM.sol create mode 100644 src/Wrapper.sol delete mode 100644 test/Foo.t.sol diff --git a/foundry.toml b/foundry.toml index ee59ba1..496963a 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,8 +3,9 @@ gas_reports = ["*"] gas_reports_ignore = [] ignored_error_codes = [] optimizer = false -solc_version = "0.8.25" +solc_version = "0.8.23" verbosity = 3 +libs = ["node_modules", "lib"] [profile.production] evm_version = "cancun" diff --git a/package-lock.json b/package-lock.json index 4ede4b7..c90307f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "@mzero-labs/foundry-template", "version": "1.0.0", + "dependencies": { + "@aptos-labs/ts-sdk": "^1.18.1" + }, "devDependencies": { "husky": "^9.0.11", "lint-staged": "^15.2.2", @@ -19,6 +22,182 @@ "node": ">=18" } }, + "node_modules/@aptos-labs/aptos-cli": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@aptos-labs/aptos-cli/-/aptos-cli-0.1.8.tgz", + "integrity": "sha512-xSWDchqoDR4aR74xNoJZgOzIFtn+EKFGGFLG0vOb+6Ce8Jgg1Ui0Pqhvwbx6Z36dDxfKv0F4M7bnurvWpwAjXA==", + "bin": { + "aptos": "bin/aptos" + } + }, + "node_modules/@aptos-labs/aptos-client": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@aptos-labs/aptos-client/-/aptos-client-0.1.0.tgz", + "integrity": "sha512-q3s6pPq8H2buGp+tPuIRInWsYOuhSEwuNJPwd2YnsiID3YSLihn2ug39ktDJAcSOprUcp7Nid8WK7hKqnUmSdA==", + "dependencies": { + "axios": "1.6.2", + "got": "^11.8.6" + }, + "engines": { + "node": ">=15.10.0" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@aptos-labs/aptos-client/node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@aptos-labs/ts-sdk": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@aptos-labs/ts-sdk/-/ts-sdk-1.18.1.tgz", + "integrity": "sha512-+tsm+UAT8BEMJsT30RpIT8rv6yDwFcs7W/YvyHPG2wnOvTjnGQe1CT8sB/qqUt4OiVhyPdddPQDB4+4oJBVyAQ==", + "dependencies": { + "@aptos-labs/aptos-cli": "^0.1.2", + "@aptos-labs/aptos-client": "^0.1.0", + "@noble/curves": "^1.4.0", + "@noble/hashes": "^1.4.0", + "@scure/bip32": "^1.4.0", + "@scure/bip39": "^1.3.0", + "eventemitter3": "^5.0.1", + "form-data": "^4.0.0", + "js-base64": "^3.7.7", + "jwt-decode": "^4.0.0", + "poseidon-lite": "^0.2.0" + }, + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/@babel/code-frame": { "version": "7.24.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", @@ -118,6 +297,28 @@ "node": ">=4" } }, + "node_modules/@noble/curves": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.0.tgz", + "integrity": "sha512-p+4cb332SFCrReJkCYe8Xzm0OWi4Jji5jVdIZRL/PmacmDkFNw6MrrV+gGpiPxLHbV+zKFRywUWbaseT+tZRXg==", + "dependencies": { + "@noble/hashes": "1.4.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -165,6 +366,39 @@ "prettier": "^3.0.0" } }, + "node_modules/@scure/base": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.6.tgz", + "integrity": "sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g==", + "funding": { + "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==", + "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/bip39": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.3.0.tgz", + "integrity": "sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ==", + "dependencies": { + "@noble/hashes": "~1.4.0", + "@scure/base": "~1.1.6" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@sindresorhus/is": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", @@ -195,11 +429,45 @@ "node": ">=14.16" } }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, "node_modules/@types/http-cache-semantics": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", - "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.14.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.2.tgz", + "integrity": "sha512-xyu6WAMVwv6AKFLB+e/7ySZVr/0zLCzOa7rSpq6jNwpqOrUbcACDWC+53d4n2QHOnDou0fbIsg8wZu/sxrnI4Q==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dependencies": { + "@types/node": "*" + } }, "node_modules/ajv": { "version": "6.12.6", @@ -283,6 +551,21 @@ "node": ">=8" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "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", @@ -483,6 +766,25 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone-response/node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -507,6 +809,17 @@ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, + "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==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", @@ -587,7 +900,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "dev": true, "dependencies": { "mimic-response": "^3.1.0" }, @@ -602,7 +914,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "dev": true, "engines": { "node": ">=10" }, @@ -623,17 +934,32 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", - "dev": true, "engines": { "node": ">=10" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -655,8 +981,7 @@ "node_modules/eventemitter3": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" }, "node_modules/execa": { "version": "8.0.1", @@ -723,6 +1048,38 @@ "node": ">=8" } }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/form-data-encoder": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", @@ -824,8 +1181,7 @@ "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==" }, "node_modules/http2-wrapper": { "version": "2.2.1", @@ -953,6 +1309,11 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -974,8 +1335,7 @@ "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -989,11 +1349,18 @@ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true }, + "node_modules/jwt-decode": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz", + "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==", + "engines": { + "node": ">=18" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, "dependencies": { "json-buffer": "3.0.1" } @@ -1237,6 +1604,25 @@ "node": ">=8.6" } }, + "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==", + "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==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", @@ -1331,7 +1717,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -1465,6 +1850,11 @@ "node": ">=4" } }, + "node_modules/poseidon-lite": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/poseidon-lite/-/poseidon-lite-0.2.0.tgz", + "integrity": "sha512-vivDZnGmz8W4G/GzVA72PXkfYStjilu83rjjUfpL4PueKcC8nfX6hCPh2XhoC5FBgC6y0TA3YuUeUo5YCcNoig==" + }, "node_modules/prettier": { "version": "3.2.5", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", @@ -1515,6 +1905,20 @@ "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", "dev": true }, + "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==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1528,7 +1932,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, "engines": { "node": ">=10" }, @@ -1590,8 +1993,7 @@ "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", - "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==" }, "node_modules/resolve-from": { "version": "4.0.0", @@ -1932,6 +2334,11 @@ "node": ">=8.0" } }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2038,8 +2445,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/yallist": { "version": "4.0.0", diff --git a/script/Foo.s.sol b/script/Foo.s.sol deleted file mode 100644 index f11a782..0000000 --- a/script/Foo.s.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.25; - -import { Script } from "forge-std/Script.sol"; -import { Foo } from "../src/Foo.sol"; - -/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting -contract FooScript is Script { - Foo internal foo; - - function run() public { - vm.startBroadcast(); - foo = new Foo(); - vm.stopBroadcast(); - } -} diff --git a/src/Foo.sol b/src/Foo.sol deleted file mode 100644 index be90d28..0000000 --- a/src/Foo.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.25; - -contract Foo { - function getFoo() external pure returns (string memory) { - return "Foo"; - } -} diff --git a/src/WM.sol b/src/WM.sol new file mode 100644 index 0000000..0ac8abc --- /dev/null +++ b/src/WM.sol @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity 0.8.23; + +import {ERC20Extended} from "../lib/common/src/ERC20Extended.sol"; +import {IERC20} from "../lib/common/src/interfaces/IERC20.sol"; +import {IMToken} from "../lib/protocol/src/interfaces/IMToken.sol"; + +interface ITTGRegistrar { + function listContains(bytes32 list, address account) external view returns (bool); +} + +contract WM is IERC20, ERC20Extended { + /* ============ Variables ============ */ + + address public immutable ttgRegistrar; + address public immutable mToken; + address public immutable wrapper; + + // Totals + uint256 public principalOfTotalEarningSupply; + uint256 public totalNonEarningSupply; + uint256 public totalSupply; + // uint256 public totalRewards; + uint256 public excessOfM; + + // TODO: storage slots is subject to massive optimization here + mapping(address account => uint256 balance) internal _balances; // for earners and non-earners + + mapping(address account => bool isEarning) internal _isEarning; + + // Earners only + mapping(address account => uint256 reward) internal _rewards; + mapping(address account => uint256 principal) internal _earningPrincipals; + mapping(address account => uint256 index) internal _lastAccrueIndices; + + uint256 public _lastExcessMAccrueIndex; + + /* ============ Errors ============ */ + + error ZeroTTGRegistrar(); + error ZeroMToken(); + error ZeroWrapper(); + error NotApprovedEarner(); + error NotWrapper(); + + modifier onlyWrapper() { + if (msg.sender != wrapper) revert NotWrapper(); + + _; + } + + /* ============ Constructor ============ */ + + constructor(address ttgRegistrar_, address mToken_, address wrapper_) ERC20Extended("WM by M^0", "WM", 6) { + if ((ttgRegistrar = ttgRegistrar_) == address(0)) revert ZeroTTGRegistrar(); + if ((mToken = mToken_) == address(0)) revert ZeroMToken(); + if ((wrapper = wrapper_) == address(0)) revert ZeroWrapper(); + } + + /* ============ Interactive Functions ============ */ + + /// 1:1 wrap + function mint(address account_, uint256 amount_) external onlyWrapper { + _mint(account_, amount_); + } + + // 1:1 unwrap + function burn(address account_, uint256 amount_) external onlyWrapper { + _burn(account_, amount_); + } + + function claimExcess(address account_, uint256 amount_) external onlyWrapper { + excessOfM -= amount_; + _mint(account_, amount_); + } + + function startEarning(address account) external { + if (!_isApprovedWEarner(account)) revert NotApprovedEarner(); + + _startEarning(account); + } + + function stopEarning(address account_) external { + if (_isApprovedWEarner(account_)) revert NotApprovedEarner(); + + _stopEarning(account_); + } + + /* ============ View/Pure Functions ============ */ + + function totalEarningSupply() public view returns (uint256) { + return principalOfTotalEarningSupply * currentIndex(); + } + + function balanceOf(address account_) external view returns (uint256 balance_) { + return _balances[account_]; + } + + function currentIndex() public view returns (uint128) { + return IMToken(mToken).currentIndex(); + } + + /* ============ Internal Interactive Functions ============ */ + + function _accrueRewards(address account_) internal { + if (!_isEarning[account_]) return; + + uint256 currentIndex_ = currentIndex(); + + if (_lastAccrueIndices[account_] == 0) { + _lastAccrueIndices[account_] = currentIndex_; + return; + } + + uint256 newReward_ = _earningPrincipals[account_] * (currentIndex_ - _lastAccrueIndices[account_]); + _rewards[account_] += newReward_; + // totalRewards += newReward_; + _lastAccrueIndices[account_] = currentIndex_; + } + + function _accrueExcessOfM() internal { + uint256 currentIndex_ = currentIndex(); + + uint256 excessSinceLastAccrue_ = principalOfTotalEarningSupply * (currentIndex_ - _lastExcessMAccrueIndex); + uint256 totalAdjustedSupply_ = totalNonEarningSupply + excessSinceLastAccrue_; + excessOfM += IMToken(mToken).balanceOf(wrapper) - totalAdjustedSupply_; + + _lastExcessMAccrueIndex = currentIndex_; + } + + function _mint(address recipient_, uint256 amount_) internal { + _accrueRewards(recipient_); + _accrueExcessOfM(); + + if (_isEarning[recipient_]) { + _addEarningAmount(recipient_, amount_); + } else { + _addNonEarningAmount(recipient_, amount_); + } + + totalSupply += amount_; + } + + function _burn(address account_, uint256 amount_) internal { + _accrueRewards(account_); + _accrueExcessOfM(); + + if (_isEarning[account_]) { + _subtractEarningAmount(account_, amount_); + } else { + _subtractNonEarningAmount(account_, amount_); + } + } + + function _startEarning(address account_) internal { + if (_isEarning[account_]) return; + + _isEarning[account_] = true; + _lastAccrueIndices[account_] = currentIndex(); + + _earningPrincipals[account_] = _balances[account_] / currentIndex(); + } + + function _stopEarning(address account_) internal { + if (!_isEarning[account_]) return; + + _accrueRewards(account_); + _accrueExcessOfM(); + + // Totals update + uint256 principalAmount_ = _earningPrincipals[account_]; + + totalNonEarningSupply += principalAmount_ * currentIndex(); + principalOfTotalEarningSupply -= principalAmount_; + + // Account update + delete _earningPrincipals[account_]; + delete _lastAccrueIndices[account_]; + _isEarning[account_] = false; + } + + function _addEarningAmount(address account_, uint256 amount_) internal { + _balances[account_] += amount_; + + uint256 principalAmount_ = amount_ / currentIndex(); + + _earningPrincipals[account_] += principalAmount_; + principalOfTotalEarningSupply += principalAmount_; + + // Update for excess of M vs wM here + } + + function _addNonEarningAmount(address account_, uint256 amount_) internal { + _balances[account_] += amount_; + totalNonEarningSupply += amount_; + } + + function _subtractEarningAmount(address account_, uint256 amount_) internal { + uint256 principalAmount_ = amount_ / currentIndex(); + + // Account update + _balances[account_] -= amount_; + _earningPrincipals[account_] -= principalAmount_; + + // Totals update + principalOfTotalEarningSupply -= principalAmount_; + + // Update for excess of M vs wM here + } + + function _subtractNonEarningAmount(address account_, uint256 amount_) internal { + // Account update + _balances[account_] -= amount_; + + // Totals update + totalNonEarningSupply -= amount_; + } + + function _transfer(address sender_, address recipient_, uint256 amount_) internal override { + _accrueRewards(sender_); + _accrueRewards(recipient_); + + if (_isEarning[sender_]) { + _subtractEarningAmount(sender_, amount_); + } else { + _subtractNonEarningAmount(sender_, amount_); + } + + if (_isEarning[recipient_]) { + _addEarningAmount(recipient_, amount_); + } else { + _addNonEarningAmount(recipient_, amount_); + } + } + + /* ============ Internal View/Pure Functions ============ */ + + function _isApprovedWEarner(address account_) internal view returns (bool) { + return ITTGRegistrar(ttgRegistrar).listContains("wm_earners_list", account_); + } +} diff --git a/src/Wrapper.sol b/src/Wrapper.sol new file mode 100644 index 0000000..b569402 --- /dev/null +++ b/src/Wrapper.sol @@ -0,0 +1,37 @@ +pragma solidity 0.8.23; + +import {IMToken} from "../lib/protocol/src/interfaces/IMToken.sol"; +import {WM} from "./WM.sol"; + +contract Wrapper { + address public immutable mToken; + address public immutable wMToken; + address public immutable excessMOwner; + + /* ============ Constructor ============ */ + + constructor(address mToken_, address wMToken_, address excessMOwner_) { + mToken = mToken_; + wMToken = wMToken_; + excessMOwner = excessMOwner_; + } + + function wrap(address account_, uint256 amount_) external { + IMToken(mToken).transfer(address(this), amount_); + WM(wMToken).mint(account_, amount_); + } + + function unwrap(address account_, uint256 amount_) external { + WM(wMToken).burn(msg.sender, amount_); + IMToken(mToken).transfer(account_, amount_); + } + + // Just example, excess of M goes somewhere else + function claimExcess(uint256 amount_) external { + WM(wMToken).claimExcess(excessMOwner, amount_); + } + + function claim(uint256 amount_) external { + // WMToken(wMToken).claim(msg.sender, amount_); + } +} diff --git a/test/Foo.t.sol b/test/Foo.t.sol deleted file mode 100644 index 8e6f3e8..0000000 --- a/test/Foo.t.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0 - -pragma solidity 0.8.25; - -import "forge-std/Test.sol"; - -import { Foo } from "../src/Foo.sol"; - -interface IERC20 { - function balanceOf(address account) external view returns (uint256); -} - -/// @dev See the "Writing Tests" section in the Foundry Book if this is your first time with Forge. -/// https://book.getfoundry.sh/forge/writing-tests -contract FooTest is Test { - uint256 public mainnetFork; - - Foo public fooContract = new Foo(); - - function setUp() public { - mainnetFork = vm.createFork(vm.rpcUrl("mainnet"), 16_428_000); - } - - /// @dev Simple test. Run Forge with `-vvvv` to see stack traces. - function test() external { - string memory foo = fooContract.getFoo(); - - assertEq(foo, "Foo"); - } - - /// @dev Test that runs against a fork of Ethereum Mainnet. You need to set `MAINNET_RPC_URL` in your `.env` - function testFork() external { - vm.selectFork(mainnetFork); - - address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; - address holder = 0x7713974908Be4BEd47172370115e8b1219F4A5f0; - uint256 actualBalance = IERC20(usdc).balanceOf(holder); - uint256 expectedBalance = 196_307_713.810457e6; - assertEq(actualBalance, expectedBalance); - } -} From 2656ce6e76e3c6d904841a609d0ff5a63ed6ca05 Mon Sep 17 00:00:00 2001 From: toninorair Date: Fri, 7 Jun 2024 16:41:26 -0400 Subject: [PATCH 02/10] Add libs --- .gitmodules | 6 ++++++ lib/common | 1 + lib/protocol | 1 + src/WM.sol | 3 ++- 4 files changed, 10 insertions(+), 1 deletion(-) create mode 160000 lib/common create mode 160000 lib/protocol diff --git a/.gitmodules b/.gitmodules index 482a2f9..82e80c6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,9 @@ path = lib/forge-std url = https://github.com/foundry-rs/forge-std branch = v1 +[submodule "lib/protocol"] + path = lib/protocol + url = https://github.com/MZero-Labs/protocol +[submodule "lib/common"] + path = lib/common + url = https://github.com/MZero-Labs/common diff --git a/lib/common b/lib/common new file mode 160000 index 0000000..e809402 --- /dev/null +++ b/lib/common @@ -0,0 +1 @@ +Subproject commit e809402c4cc21f1fa8291f17ee0aee859f3b0d29 diff --git a/lib/protocol b/lib/protocol new file mode 160000 index 0000000..b1c6e62 --- /dev/null +++ b/lib/protocol @@ -0,0 +1 @@ +Subproject commit b1c6e624ed09a9e28f4ae45cd87fda610fafe446 diff --git a/src/WM.sol b/src/WM.sol index 0ac8abc..9450762 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -42,6 +42,7 @@ contract WM is IERC20, ERC20Extended { error ZeroMToken(); error ZeroWrapper(); error NotApprovedEarner(); + error IsApprovedEarner(); error NotWrapper(); modifier onlyWrapper() { @@ -82,7 +83,7 @@ contract WM is IERC20, ERC20Extended { } function stopEarning(address account_) external { - if (_isApprovedWEarner(account_)) revert NotApprovedEarner(); + if (_isApprovedWEarner(account_)) revert IsApprovedEarner(); _stopEarning(account_); } From 79b97f3aac56a88c65355a19f47a53afe1a53045 Mon Sep 17 00:00:00 2001 From: toninorair Date: Fri, 7 Jun 2024 16:51:21 -0400 Subject: [PATCH 03/10] Add missing accrueExcess --- src/WM.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/WM.sol b/src/WM.sol index 9450762..9cc559c 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -157,6 +157,8 @@ contract WM is IERC20, ERC20Extended { function _startEarning(address account_) internal { if (_isEarning[account_]) return; + _accrueExcessOfM(); + _isEarning[account_] = true; _lastAccrueIndices[account_] = currentIndex(); @@ -221,6 +223,7 @@ contract WM is IERC20, ERC20Extended { function _transfer(address sender_, address recipient_, uint256 amount_) internal override { _accrueRewards(sender_); _accrueRewards(recipient_); + _accrueExcessOfM(); if (_isEarning[sender_]) { _subtractEarningAmount(sender_, amount_); From 1f961117b06feaeeef8d68a1c3ede53919a90470 Mon Sep 17 00:00:00 2001 From: toninorair Date: Sun, 9 Jun 2024 23:47:15 -0400 Subject: [PATCH 04/10] Add fix for excess M --- src/WM.sol | 36 ++++++++++-------------------------- src/Wrapper.sol | 7 ++++++- 2 files changed, 16 insertions(+), 27 deletions(-) diff --git a/src/WM.sol b/src/WM.sol index 9cc559c..eb45f62 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -21,8 +21,6 @@ contract WM is IERC20, ERC20Extended { uint256 public principalOfTotalEarningSupply; uint256 public totalNonEarningSupply; uint256 public totalSupply; - // uint256 public totalRewards; - uint256 public excessOfM; // TODO: storage slots is subject to massive optimization here mapping(address account => uint256 balance) internal _balances; // for earners and non-earners @@ -31,11 +29,10 @@ contract WM is IERC20, ERC20Extended { // Earners only mapping(address account => uint256 reward) internal _rewards; + mapping(address account => uint256 principal) internal _earningPrincipals; mapping(address account => uint256 index) internal _lastAccrueIndices; - uint256 public _lastExcessMAccrueIndex; - /* ============ Errors ============ */ error ZeroTTGRegistrar(); @@ -71,11 +68,6 @@ contract WM is IERC20, ERC20Extended { _burn(account_, amount_); } - function claimExcess(address account_, uint256 amount_) external onlyWrapper { - excessOfM -= amount_; - _mint(account_, amount_); - } - function startEarning(address account) external { if (!_isApprovedWEarner(account)) revert NotApprovedEarner(); @@ -102,6 +94,12 @@ contract WM is IERC20, ERC20Extended { return IMToken(mToken).currentIndex(); } + function excessOfM() external view returns (uint256) { + uint256 totalSupply_ = totalNonEarningSupply + totalEarningSupply(); + + return IMToken(mToken).balanceOf(wrapper) - totalSupply_; + } + /* ============ Internal Interactive Functions ============ */ function _accrueRewards(address account_) internal { @@ -115,24 +113,15 @@ contract WM is IERC20, ERC20Extended { } uint256 newReward_ = _earningPrincipals[account_] * (currentIndex_ - _lastAccrueIndices[account_]); - _rewards[account_] += newReward_; - // totalRewards += newReward_; - _lastAccrueIndices[account_] = currentIndex_; - } - function _accrueExcessOfM() internal { - uint256 currentIndex_ = currentIndex(); + // Update only _balances[account_], do not use _mint + _balances[account_] += newReward_; - uint256 excessSinceLastAccrue_ = principalOfTotalEarningSupply * (currentIndex_ - _lastExcessMAccrueIndex); - uint256 totalAdjustedSupply_ = totalNonEarningSupply + excessSinceLastAccrue_; - excessOfM += IMToken(mToken).balanceOf(wrapper) - totalAdjustedSupply_; - - _lastExcessMAccrueIndex = currentIndex_; + _lastAccrueIndices[account_] = currentIndex_; } function _mint(address recipient_, uint256 amount_) internal { _accrueRewards(recipient_); - _accrueExcessOfM(); if (_isEarning[recipient_]) { _addEarningAmount(recipient_, amount_); @@ -145,7 +134,6 @@ contract WM is IERC20, ERC20Extended { function _burn(address account_, uint256 amount_) internal { _accrueRewards(account_); - _accrueExcessOfM(); if (_isEarning[account_]) { _subtractEarningAmount(account_, amount_); @@ -157,8 +145,6 @@ contract WM is IERC20, ERC20Extended { function _startEarning(address account_) internal { if (_isEarning[account_]) return; - _accrueExcessOfM(); - _isEarning[account_] = true; _lastAccrueIndices[account_] = currentIndex(); @@ -169,7 +155,6 @@ contract WM is IERC20, ERC20Extended { if (!_isEarning[account_]) return; _accrueRewards(account_); - _accrueExcessOfM(); // Totals update uint256 principalAmount_ = _earningPrincipals[account_]; @@ -223,7 +208,6 @@ contract WM is IERC20, ERC20Extended { function _transfer(address sender_, address recipient_, uint256 amount_) internal override { _accrueRewards(sender_); _accrueRewards(recipient_); - _accrueExcessOfM(); if (_isEarning[sender_]) { _subtractEarningAmount(sender_, amount_); diff --git a/src/Wrapper.sol b/src/Wrapper.sol index b569402..1017bc9 100644 --- a/src/Wrapper.sol +++ b/src/Wrapper.sol @@ -6,7 +6,10 @@ import {WM} from "./WM.sol"; contract Wrapper { address public immutable mToken; address public immutable wMToken; + + // Excess of M claim fields address public immutable excessMOwner; + uint256 public claimedExcessM; /* ============ Constructor ============ */ @@ -28,7 +31,9 @@ contract Wrapper { // Just example, excess of M goes somewhere else function claimExcess(uint256 amount_) external { - WM(wMToken).claimExcess(excessMOwner, amount_); + uint256 excess_ = WM(wMToken).excessOfM() - claimedExcessM; + claimedExcessM += excess_; + IMToken(mToken).transfer(excessMOwner, amount_); } function claim(uint256 amount_) external { From b3f4580ea492905f3d242e0478649625124ea789 Mon Sep 17 00:00:00 2001 From: toninorair Date: Mon, 10 Jun 2024 09:59:37 -0400 Subject: [PATCH 05/10] Claim excess --- src/Wrapper.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Wrapper.sol b/src/Wrapper.sol index 1017bc9..3de999c 100644 --- a/src/Wrapper.sol +++ b/src/Wrapper.sol @@ -9,7 +9,7 @@ contract Wrapper { // Excess of M claim fields address public immutable excessMOwner; - uint256 public claimedExcessM; + // uint256 public claimedExcessM; /* ============ Constructor ============ */ @@ -31,9 +31,9 @@ contract Wrapper { // Just example, excess of M goes somewhere else function claimExcess(uint256 amount_) external { - uint256 excess_ = WM(wMToken).excessOfM() - claimedExcessM; - claimedExcessM += excess_; - IMToken(mToken).transfer(excessMOwner, amount_); + if (amount_ <= WM(wMToken).excessOfM()) { + IMToken(mToken).transfer(excessMOwner, amount_); + } } function claim(uint256 amount_) external { From aa195abe2aaf91721ce86254f8f7bb83190a3ece Mon Sep 17 00:00:00 2001 From: toninorair Date: Mon, 10 Jun 2024 11:29:03 -0400 Subject: [PATCH 06/10] Add rewards instead of balance increase --- src/WM.sol | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/WM.sol b/src/WM.sol index eb45f62..d36071b 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -113,9 +113,7 @@ contract WM is IERC20, ERC20Extended { } uint256 newReward_ = _earningPrincipals[account_] * (currentIndex_ - _lastAccrueIndices[account_]); - - // Update only _balances[account_], do not use _mint - _balances[account_] += newReward_; + _rewards[account_] += newReward_; _lastAccrueIndices[account_] = currentIndex_; } @@ -175,8 +173,6 @@ contract WM is IERC20, ERC20Extended { _earningPrincipals[account_] += principalAmount_; principalOfTotalEarningSupply += principalAmount_; - - // Update for excess of M vs wM here } function _addNonEarningAmount(address account_, uint256 amount_) internal { @@ -193,8 +189,6 @@ contract WM is IERC20, ERC20Extended { // Totals update principalOfTotalEarningSupply -= principalAmount_; - - // Update for excess of M vs wM here } function _subtractNonEarningAmount(address account_, uint256 amount_) internal { @@ -222,6 +216,15 @@ contract WM is IERC20, ERC20Extended { } } + // function _claim(address earner_, uint256 amount_) internal { + // _accrueRewards(earner_); + + // address claimer_ = _claimers[earner_]; + + // // _mint(claimer_, amount_); + // _balances[claimer_] += amount_; // do not update principal if claimer is an earner? + // } + /* ============ Internal View/Pure Functions ============ */ function _isApprovedWEarner(address account_) internal view returns (bool) { From 0e919f42e4448d30b2743d5d734e816b07e1d636 Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 11 Jun 2024 20:56:00 -0400 Subject: [PATCH 07/10] Add fixes, missed statements --- src/WM.sol | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/WM.sol b/src/WM.sol index d36071b..9e8b600 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -95,9 +95,9 @@ contract WM is IERC20, ERC20Extended { } function excessOfM() external view returns (uint256) { - uint256 totalSupply_ = totalNonEarningSupply + totalEarningSupply(); + uint256 totalProjectedSupply_ = totalNonEarningSupply + totalEarningSupply(); - return IMToken(mToken).balanceOf(wrapper) - totalSupply_; + return IMToken(mToken).balanceOf(wrapper) - totalProjectedSupply_; } /* ============ Internal Interactive Functions ============ */ @@ -114,7 +114,6 @@ contract WM is IERC20, ERC20Extended { uint256 newReward_ = _earningPrincipals[account_] * (currentIndex_ - _lastAccrueIndices[account_]); _rewards[account_] += newReward_; - _lastAccrueIndices[account_] = currentIndex_; } @@ -138,15 +137,25 @@ contract WM is IERC20, ERC20Extended { } else { _subtractNonEarningAmount(account_, amount_); } + + totalSupply -= amount_; } function _startEarning(address account_) internal { if (_isEarning[account_]) return; + // Account update _isEarning[account_] = true; _lastAccrueIndices[account_] = currentIndex(); - _earningPrincipals[account_] = _balances[account_] / currentIndex(); + uint256 amount_ = _balances[account_]; + uint256 principalAmount_ = amount_ / currentIndex(); + + _earningPrincipals[account_] = principalAmount_; + + // Totals update + principalOfTotalEarningSupply += principalAmount_; + totalNonEarningSupply -= amount; } function _stopEarning(address account_) internal { @@ -216,15 +225,6 @@ contract WM is IERC20, ERC20Extended { } } - // function _claim(address earner_, uint256 amount_) internal { - // _accrueRewards(earner_); - - // address claimer_ = _claimers[earner_]; - - // // _mint(claimer_, amount_); - // _balances[claimer_] += amount_; // do not update principal if claimer is an earner? - // } - /* ============ Internal View/Pure Functions ============ */ function _isApprovedWEarner(address account_) internal view returns (bool) { From ebad50c897dcb523a02b9544a580878ed1425dec Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 11 Jun 2024 21:29:02 -0400 Subject: [PATCH 08/10] Simplify --- src/WM.sol | 63 +++++++++++++++++++++++++++++-------------------- src/Wrapper.sol | 42 --------------------------------- 2 files changed, 37 insertions(+), 68 deletions(-) delete mode 100644 src/Wrapper.sol diff --git a/src/WM.sol b/src/WM.sol index 9e8b600..101b3e7 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -15,7 +15,7 @@ contract WM is IERC20, ERC20Extended { address public immutable ttgRegistrar; address public immutable mToken; - address public immutable wrapper; + address public immutable distributionVault; // distribute all excess of MToken // Totals uint256 public principalOfTotalEarningSupply; @@ -37,35 +37,31 @@ contract WM is IERC20, ERC20Extended { error ZeroTTGRegistrar(); error ZeroMToken(); - error ZeroWrapper(); + error ZeroDistributionVault(); error NotApprovedEarner(); error IsApprovedEarner(); error NotWrapper(); - modifier onlyWrapper() { - if (msg.sender != wrapper) revert NotWrapper(); - - _; - } - /* ============ Constructor ============ */ - constructor(address ttgRegistrar_, address mToken_, address wrapper_) ERC20Extended("WM by M^0", "WM", 6) { + constructor(address ttgRegistrar_, address mToken_, address distributionVault_) + ERC20Extended("WM by M^0", "WM", 6) + { if ((ttgRegistrar = ttgRegistrar_) == address(0)) revert ZeroTTGRegistrar(); if ((mToken = mToken_) == address(0)) revert ZeroMToken(); - if ((wrapper = wrapper_) == address(0)) revert ZeroWrapper(); + if ((distributionVault = distributionVault_) == address(0)) revert ZeroDistributionVault(); } /* ============ Interactive Functions ============ */ - /// 1:1 wrap - function mint(address account_, uint256 amount_) external onlyWrapper { + function wrap(address account_, uint256 amount_) external { + IMToken(mToken).transferFrom(msg.sender, address(this), amount_); _mint(account_, amount_); } - // 1:1 unwrap - function burn(address account_, uint256 amount_) external onlyWrapper { - _burn(account_, amount_); + function unwrap(address account_, uint256 amount_) external { + _burn(msg.sender, amount_); + IMToken(mToken).transfer(account_, amount_); } function startEarning(address account) external { @@ -80,24 +76,41 @@ contract WM is IERC20, ERC20Extended { _stopEarning(account_); } - /* ============ View/Pure Functions ============ */ + // TODO add claim forwarding logic instead of recipient + function claimRewards(address recipient) external { + _accrueRewards(msg.sender); - function totalEarningSupply() public view returns (uint256) { - return principalOfTotalEarningSupply * currentIndex(); + uint256 claimableAmount_ = _rewards[msg.sender]; + + if (claimableAmount_ == 0) return; + + _rewards[msg.sender] = 0; + + IMToken(mToken).transfer(recipient, claimableAmount_); } + function claimExcessToDistributionVault() external { + IMToken(mToken).transfer(distributionVault, excessOfM()); + } + + /* ============ View/Pure Functions ============ */ + function balanceOf(address account_) external view returns (uint256 balance_) { return _balances[account_]; } + function totalEarningSupply() public view returns (uint256) { + return principalOfTotalEarningSupply * currentIndex(); + } + function currentIndex() public view returns (uint128) { return IMToken(mToken).currentIndex(); } - function excessOfM() external view returns (uint256) { + function excessOfM() public view returns (uint256) { uint256 totalProjectedSupply_ = totalNonEarningSupply + totalEarningSupply(); - return IMToken(mToken).balanceOf(wrapper) - totalProjectedSupply_; + return IMToken(mToken).balanceOf(address(this)) - totalProjectedSupply_; } /* ============ Internal Interactive Functions ============ */ @@ -107,11 +120,6 @@ contract WM is IERC20, ERC20Extended { uint256 currentIndex_ = currentIndex(); - if (_lastAccrueIndices[account_] == 0) { - _lastAccrueIndices[account_] = currentIndex_; - return; - } - uint256 newReward_ = _earningPrincipals[account_] * (currentIndex_ - _lastAccrueIndices[account_]); _rewards[account_] += newReward_; _lastAccrueIndices[account_] = currentIndex_; @@ -155,7 +163,7 @@ contract WM is IERC20, ERC20Extended { // Totals update principalOfTotalEarningSupply += principalAmount_; - totalNonEarningSupply -= amount; + totalNonEarningSupply -= amount_; } function _stopEarning(address account_) internal { @@ -185,7 +193,10 @@ contract WM is IERC20, ERC20Extended { } function _addNonEarningAmount(address account_, uint256 amount_) internal { + // Account update _balances[account_] += amount_; + + // Totals update totalNonEarningSupply += amount_; } diff --git a/src/Wrapper.sol b/src/Wrapper.sol deleted file mode 100644 index 3de999c..0000000 --- a/src/Wrapper.sol +++ /dev/null @@ -1,42 +0,0 @@ -pragma solidity 0.8.23; - -import {IMToken} from "../lib/protocol/src/interfaces/IMToken.sol"; -import {WM} from "./WM.sol"; - -contract Wrapper { - address public immutable mToken; - address public immutable wMToken; - - // Excess of M claim fields - address public immutable excessMOwner; - // uint256 public claimedExcessM; - - /* ============ Constructor ============ */ - - constructor(address mToken_, address wMToken_, address excessMOwner_) { - mToken = mToken_; - wMToken = wMToken_; - excessMOwner = excessMOwner_; - } - - function wrap(address account_, uint256 amount_) external { - IMToken(mToken).transfer(address(this), amount_); - WM(wMToken).mint(account_, amount_); - } - - function unwrap(address account_, uint256 amount_) external { - WM(wMToken).burn(msg.sender, amount_); - IMToken(mToken).transfer(account_, amount_); - } - - // Just example, excess of M goes somewhere else - function claimExcess(uint256 amount_) external { - if (amount_ <= WM(wMToken).excessOfM()) { - IMToken(mToken).transfer(excessMOwner, amount_); - } - } - - function claim(uint256 amount_) external { - // WMToken(wMToken).claim(msg.sender, amount_); - } -} From d72d974f9b8ceb2bf4a84e207f83139926ba9f9b Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 11 Jun 2024 21:54:50 -0400 Subject: [PATCH 09/10] Add simple claim --- src/WM.sol | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/WM.sol b/src/WM.sol index 101b3e7..7fea015 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -8,6 +8,8 @@ import {IMToken} from "../lib/protocol/src/interfaces/IMToken.sol"; interface ITTGRegistrar { function listContains(bytes32 list, address account) external view returns (bool); + + function getClaimer(bytes32 list, address earner) external view returns (address claimer); } contract WM is IERC20, ERC20Extended { @@ -17,6 +19,9 @@ contract WM is IERC20, ERC20Extended { address public immutable mToken; address public immutable distributionVault; // distribute all excess of MToken + bytes32 public constant WM_EARNERS_LIST = "wm_earners_list"; + bytes32 public constant WM_EARNER_CLAIMER_LIST = "wm_earner_claimer_list"; + // Totals uint256 public principalOfTotalEarningSupply; uint256 public totalNonEarningSupply; @@ -76,17 +81,16 @@ contract WM is IERC20, ERC20Extended { _stopEarning(account_); } - // TODO add claim forwarding logic instead of recipient - function claimRewards(address recipient) external { - _accrueRewards(msg.sender); + function claimRewardsForEarner(address earner) external { + _accrueRewards(earner); uint256 claimableAmount_ = _rewards[msg.sender]; - if (claimableAmount_ == 0) return; - _rewards[msg.sender] = 0; - IMToken(mToken).transfer(recipient, claimableAmount_); + IMToken(mToken).transfer(_getClaimer(earner), claimableAmount_); + // OR + // _mint(_getClaimer(earner), claimableAmount_); } function claimExcessToDistributionVault() external { @@ -239,6 +243,10 @@ contract WM is IERC20, ERC20Extended { /* ============ Internal View/Pure Functions ============ */ function _isApprovedWEarner(address account_) internal view returns (bool) { - return ITTGRegistrar(ttgRegistrar).listContains("wm_earners_list", account_); + return ITTGRegistrar(ttgRegistrar).listContains(WM_EARNERS_LIST, account_); + } + + function _getClaimer(address earner_) internal view returns (address) { + return ITTGRegistrar(ttgRegistrar).getClaimer(WM_EARNER_CLAIMER_LIST, earner_); } } From 8b36240f4db268766fdf6a13895f42398c878e40 Mon Sep 17 00:00:00 2001 From: toninorair Date: Tue, 11 Jun 2024 22:41:00 -0400 Subject: [PATCH 10/10] remove error --- src/WM.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/WM.sol b/src/WM.sol index 7fea015..9cd3967 100644 --- a/src/WM.sol +++ b/src/WM.sol @@ -45,7 +45,6 @@ contract WM is IERC20, ERC20Extended { error ZeroDistributionVault(); error NotApprovedEarner(); error IsApprovedEarner(); - error NotWrapper(); /* ============ Constructor ============ */