├── .env.example ├── .gitignore ├── .slugignore ├── LICENSE ├── Procfile ├── README.md ├── audits ├── first-audit.pdf └── second-audit.pdf ├── contracts ├── ao │ └── bridge.lua ├── evm │ ├── bridge.sol │ └── bridge_usdc.sol └── mem │ ├── bridge.js │ └── bridge.json ├── img ├── lock.png └── unlock.png ├── package-lock.json ├── package.json └── src ├── index.js └── utils ├── aoMint.js ├── constants.js ├── ethTx.js └── memTx.js /.env.example: -------------------------------------------------------------------------------- 1 | JWK=... 2 | CRONJOB_PK=... -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port -------------------------------------------------------------------------------- /.slugignore: -------------------------------------------------------------------------------- 1 | /audits 2 | /img -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 mem.tech 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: node ./src/index.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

@mem-fdn/mem-bridge

6 |

Bridging ERC20 Tokens to MEM Protocol & AO network

7 |

8 | 9 | ## Abstract 10 | 11 | This repository focuses on a design for a lock-mint ERC20 bridge to the [MEM protocol](https://mem.tech) and [AO network](https://ao.arweave.dev). The bridge's tech stack utilizes MEM serverless functions, MEM molecules, EVM smart contracts, Chainlink's AnyAPI and AO processes. 12 | 13 | ***This is a experimental bridge, audited but not tested on mainnet, don't use it in production*** 14 | 15 | ## Build and Run 16 | 17 | ```bash 18 | git clone https://github.com/mem-fdn/mem-bridge.git 19 | 20 | cd mem-bridge 21 | 22 | npm install && npm run start 23 | ``` 24 | 25 | ### To interact with MEM protocol 26 | 27 | ```bash 28 | npm i -g mem-cli-js 29 | ``` 30 | 31 | And to deploy the `/contracts/mem` functions: 32 | 33 | ```bash 34 | cd contracts 35 | 36 | cd mem 37 | 38 | mem deploy --src ./bridge.js --init-state bridge.json 39 | ``` 40 | 41 | ## Changes Required for Testing 42 | 43 | If you intend to modify and test the code yourself, please ensure to follow these steps: 44 | 45 | 1. Update the new `bridge.sol` contract address in the `/src/utils/constants.js` file. 46 | 47 | 2. Update the bridge address in the MEM function state (and redeploy it) located at `./contracts/mem/state.json`. 48 | 49 | 3. After redeploying the MEM changes, obtain a new function ID that you must update again in `/src/utils/constants.js`. 50 | 51 | 4. If you change the API endpoint, make sure to update it in the `./contracts/evm/bridge.sol` contract. 52 | 53 | These modifications are essential to ensure that the API, which Chainlink interacts with, hosted under [0xmem.net](https://0xmem.net), accurately reads the token changes. 54 | 55 | ## Lock Flow 56 | 57 | ![lock](./img/lock.png) 58 | 59 | ## Unlock Flow 60 | 61 | ![unlock](./img/unlock.png) 62 | 63 | ## How It Works (Transaction Lifecycle for Bridging) 64 | 65 | This section elucidates the transaction lifecycle of a lock-mint process: 66 | 67 | 1. **User Initiates Locking:** 68 | - The user calls the `lock()` function from `bridge.sol`. 69 | 70 | 2. **Verification and Execution:** 71 | - Dapps retrieve the resulting TXID from step 1 and invoke the `executeLock()` function in the `bridge.js` serverless function. 72 | - This function verifies the data from the TXID of the lock function using the MEM molecule (`/vl/` server endpoint). 73 | 74 | 3. **Mirror of Token Balance:** 75 | - The `executeLock()` MEM function mirrors the token balance locked in `bridge.sol`, effectively bridging tokens to the MEM protocol. 76 | 77 | 4. **Initiating Unlock Process:** 78 | - To bridge tokens back to the EVM network, the user calls the `initiateUnlock()` function in `bridge.js`, providing a unique `caller` for authentication and assigning it to the `mid` or `memid` data property. 79 | - This function removes the caller's balance from circulation on the MEM side. 80 | 81 | 5. **Unlock Validation:** 82 | - The dapp retrieves the resulting `mid` from `./mem/bridge.json` and calls (a cronjob, admin function) the `validateUnlock()` function in `bridge.sol`. 83 | - This function utilizes Chainlink's AnyAPI node to read back from MEM function state, verifying the issued unlock status and authorized amount. It also verifies the `caller` with `msg.sender`. 84 | - Additionally, it maps the oracle request ID to `msg.sender` (`reqToCaller()`), the unique request ID to the unique `mid` (`reqToMemId`), and the `mid` to redeeming status (`midIsRedeemed`). 85 | 86 | 6. **Fulfillment by Chainlink Node:** 87 | - The Chainlink node invokes the `fulfill()` function to update the contract state variables (maps) with the authorized token unlock amount for the given `mid` or `memid` per caller (`msg.sender`). 88 | 89 | 7. **Execution of Unlock:** 90 | - The user retrieves the request ID from the TXID resulting from `validateUnlock()` and then calls `executeUnlock()` on the solidity contract. 91 | - The `executeUnlock()` function unlocks the token on EVM based on the validity of the initial `memid` per request ID. 92 | 93 | ### Note: 94 | 95 | - The contract utilizes [LinkWell Nodes](https://linkwellnodes.io/) as the Chainlink oracle provider. 96 | - The `constructor()` function is commented with initial values used for testing purposes. 97 | - The bridge has been tested to bridge Sepolia USDC token ($USDC - `0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238`). 98 | - The current repository version has been tested using [0x842b64bBA4D3bc5Cb29A7Bf73813a01CF684AF4a](https://sepolia.etherscan.io/address/0x842b64bBA4D3bc5Cb29A7Bf73813a01CF684AF4a) for `bridge.sol`, [1dvxnlerOzF4hrFxlbOV57IHyrxSHMUTiMdtUNsWUgY](https://api.mem.tech/api/state/1dvxnlerOzF4hrFxlbOV57IHyrxSHMUTiMdtUNsWUgY) for `bridge.js`, [oDMJXlSOhJ6UjH5i7Dl-UOr_dhS1rQCX4r9ws0jvFps](https://ao.link/token/oDMJXlSOhJ6UjH5i7Dl-UOr_dhS1rQCX4r9ws0jvFps) for `bridge.lua`, and [0xmem.net](https://0xmem.net) for the API called by Chainlink. 99 | 100 | ## Disclaimer 101 | 102 | ***This is an experimental bridge, and it has not undergone testing on the mainnet. Do not to use it in production environments.*** 103 | 104 | ## License 105 | This repository is licensed under the [MIT License](./LICENSE) -------------------------------------------------------------------------------- /audits/first-audit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mem-fdn/mem-bridge/a1fac2cec9767e46be0ce65949216a48035fb3d0/audits/first-audit.pdf -------------------------------------------------------------------------------- /audits/second-audit.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mem-fdn/mem-bridge/a1fac2cec9767e46be0ce65949216a48035fb3d0/audits/second-audit.pdf -------------------------------------------------------------------------------- /contracts/ao/bridge.lua: -------------------------------------------------------------------------------- 1 | local bint = require(".bint")(256) 2 | local ao = require("ao") 3 | local json = require("json") 4 | --[[ 5 | This module implements the ao Standard Token Specification along with mint/burn 6 | functionality that facilitates assets brdiging from the MEM Bridge 7 | 8 | Terms: 9 | Sender: the wallet or Process that sent the Message 10 | 11 | It will first initialize the internal state, and then attach handlers, 12 | according to the ao Standard Token Spec API: 13 | 14 | - Info(): return the token parameters, like Name, Ticker, Logo, and Denomination 15 | 16 | - Balance(Target?: string): return the token balance of the Target. If Target is not provided, the Sender 17 | is assumed to be the Target 18 | 19 | - Balances(): return the token balance of all participants 20 | 21 | - Transfer(Target: string, Quantity: number): if the Sender has a sufficient balance, send the specified Quantity 22 | to the Target. It will also issue a Credit-Notice to the Target and a Debit-Notice to the Sender 23 | 24 | - Mint(Quantity: number, Address: string, MemId: string): if the Sender matches the Process Owner, then mint tokens to the supplied Address 25 | and MEM lock ID MemId 26 | 27 | - Burn(Quantity: number, MemTarget: string): Burn the Quantity from the Sender balance and associate it with a MEM Target (ETH EOA) to mint 28 | it back on the MEM Bridge for MemTarget 29 | 30 | - GetBurnReqs(): return all of the BurnReqs table 31 | ]] 32 | -- 33 | 34 | --[[ 35 | Initialize State 36 | 37 | ao.id is equal to the Process.Id 38 | ]] 39 | -- 40 | if not Balances then 41 | Balances = {} 42 | end 43 | 44 | if not BurnReqs then 45 | BurnReqs = {} 46 | end 47 | 48 | if not MemIds then 49 | MemIds = {} 50 | end 51 | 52 | if Name ~= "MEM Wrapped USDC" then 53 | Name = "MEM Wrapped USDC" 54 | end 55 | 56 | if Ticker ~= "mwUSDC" then 57 | Ticker = "mwUSDC" 58 | end 59 | 60 | if Denomination ~= 6 then 61 | Denomination = 6 62 | end 63 | 64 | if Logo ~= "JEkSOM_2dzs1esLXHsfD9a9sfBP8B59vvjOrXMYAVz0" then 65 | Logo = "JEkSOM_2dzs1esLXHsfD9a9sfBP8B59vvjOrXMYAVz0" 66 | end 67 | 68 | if AdminAddr ~= "vZY2XY1RD9HIfWi8ift-1_DnHLDadZMWrufSh-_rKF0" then 69 | AdminAddr = "vZY2XY1RD9HIfWi8ift-1_DnHLDadZMWrufSh-_rKF0" 70 | end 71 | 72 | --[[ 73 | Add handlers for each incoming Action defined by the ao Standard Token Specification 74 | ]] 75 | -- 76 | 77 | --[[ 78 | Info 79 | ]] 80 | -- 81 | Handlers.add( 82 | "info", 83 | Handlers.utils.hasMatchingTag("Action", "Info"), 84 | function(msg) 85 | ao.send( 86 | { 87 | Target = msg.From, 88 | Name = Name, 89 | Ticker = Ticker, 90 | Logo = Logo, 91 | Denomination = tostring(Denomination), 92 | AdminAddr = AdminAddr, 93 | } 94 | ) 95 | end 96 | ) 97 | 98 | --[[ 99 | GetBurnReqs 100 | ]] 101 | -- 102 | Handlers.add( 103 | "getBurnReqs", 104 | Handlers.utils.hasMatchingTag("Action", "GetBurnReqs"), 105 | function(msg) 106 | ao.send( 107 | { 108 | Target = msg.From, 109 | Data = json.encode(BurnReqs) 110 | } 111 | ) 112 | end 113 | ) 114 | 115 | --[[ 116 | GetMemIds 117 | ]] 118 | -- 119 | Handlers.add( 120 | "getMemIds", 121 | Handlers.utils.hasMatchingTag("Action", "GetMemIds"), 122 | function(msg) 123 | ao.send( 124 | { 125 | Target = msg.From, 126 | Data = json.encode(MemIds) 127 | } 128 | ) 129 | end 130 | ) 131 | --[[ 132 | Balance 133 | ]] 134 | -- 135 | Handlers.add( 136 | "balance", 137 | Handlers.utils.hasMatchingTag("Action", "Balance"), 138 | function(msg) 139 | local bal = "0" 140 | 141 | -- If not Recipient is provided, then return the Senders balance 142 | if (msg.Tags.Recipient and Balances[msg.Tags.Recipient]) then 143 | bal = Balances[msg.Tags.Recipient] 144 | elseif Balances[msg.From] then 145 | bal = Balances[msg.From] 146 | end 147 | 148 | ao.send( 149 | { 150 | Target = msg.From, 151 | Balance = bal, 152 | Ticker = Ticker, 153 | Account = msg.Tags.Recipient or msg.From, 154 | Data = bal 155 | } 156 | ) 157 | end 158 | ) 159 | 160 | --[[ 161 | Balances 162 | ]] 163 | -- 164 | Handlers.add( 165 | "balances", 166 | Handlers.utils.hasMatchingTag("Action", "Balances"), 167 | function(msg) 168 | ao.send({Target = msg.From, Data = json.encode(Balances)}) 169 | end 170 | ) 171 | 172 | --[[ 173 | Transfer 174 | ]] 175 | -- 176 | Handlers.add( 177 | "transfer", 178 | Handlers.utils.hasMatchingTag("Action", "Transfer"), 179 | function(msg) 180 | assert(type(msg.Recipient) == "string", "Recipient is required!") 181 | assert(type(msg.Quantity) == "string", "Quantity is required!") 182 | assert(bint.__lt(0, bint(msg.Quantity)), "Quantity must be greater than 0") 183 | 184 | if not Balances[msg.From] then 185 | Balances[msg.From] = "0" 186 | end 187 | if not Balances[msg.Recipient] then 188 | Balances[msg.Recipient] = "0" 189 | end 190 | 191 | local qty = bint(msg.Quantity) 192 | local balance = bint(Balances[msg.From]) 193 | if bint.__le(qty, balance) then 194 | Balances[msg.From] = tostring(bint.__sub(balance, qty)) 195 | Balances[msg.Recipient] = tostring(bint.__add(Balances[msg.Recipient], qty)) 196 | 197 | --[[ 198 | Only send the notifications to the Sender and Recipient 199 | if the Cast tag is not set on the Transfer message 200 | ]] 201 | -- 202 | if not msg.Cast then 203 | -- Send Debit-Notice to the Sender 204 | ao.send( 205 | { 206 | Target = msg.From, 207 | Action = "Debit-Notice", 208 | Recipient = msg.Recipient, 209 | Quantity = tostring(qty), 210 | Data = Colors.gray .. 211 | "You transferred " .. 212 | Colors.blue .. 213 | msg.Quantity .. 214 | Colors.gray .. " to " .. Colors.green .. msg.Recipient .. Colors.reset 215 | } 216 | ) 217 | -- Send Credit-Notice to the Recipient 218 | ao.send( 219 | { 220 | Target = msg.Recipient, 221 | Action = "Credit-Notice", 222 | Sender = msg.From, 223 | Quantity = tostring(qty), 224 | Data = Colors.gray .. 225 | "You received " .. 226 | Colors.blue .. 227 | msg.Quantity .. 228 | Colors.gray .. " from " .. Colors.green .. msg.Recipient .. Colors.reset 229 | } 230 | ) 231 | end 232 | else 233 | ao.send( 234 | { 235 | Target = msg.From, 236 | Action = "Transfer-Error", 237 | ["Message-Id"] = msg.Id, 238 | Error = "Insufficient Balance!" 239 | } 240 | ) 241 | end 242 | end 243 | ) 244 | 245 | --[[ 246 | Add handlers for the MEM Bridge functionality 247 | ]] 248 | -- 249 | 250 | --[[ 251 | Mint 252 | ]] 253 | -- 254 | Handlers.add( 255 | "mint", 256 | Handlers.utils.hasMatchingTag("Action", "Mint"), 257 | function(msg) 258 | assert(type(msg.Quantity) == "string", "Quantity is required!") 259 | assert(type(msg.Address) == "string", "Address required!") 260 | assert(type(msg.MemId) == "string", "MemId is Required!") 261 | assert(MemIds[msg.MemId] == nil, "MemId already exists!") 262 | assert(bint.__lt(0, msg.Quantity), "Quantity must be greater than zero!") 263 | 264 | local address = msg.Address 265 | if not Balances[address] then 266 | Balances[address] = "0" 267 | end 268 | 269 | if msg.From == AdminAddr then 270 | Balances[address] = tostring(bint.__add(Balances[address], msg.Quantity)) 271 | MemIds[msg.MemId] = true 272 | 273 | ao.send( 274 | { 275 | Target = address, 276 | Data = Colors.gray .. "Successfully minted " .. Colors.blue .. msg.Quantity .. Colors.reset 277 | } 278 | ) 279 | else 280 | ao.send( 281 | { 282 | Target = address, 283 | Action = "Mint-Error", 284 | ["Message-Id"] = msg.Id, 285 | Error = "Only the Bridge Admin can mint new " .. Ticker .. " tokens!" 286 | } 287 | ) 288 | end 289 | end 290 | ) 291 | 292 | --[[ 293 | Burn 294 | ]] 295 | -- 296 | Handlers.add( 297 | "burn", 298 | Handlers.utils.hasMatchingTag("Action", "Burn"), 299 | function(msg) 300 | assert(type(msg.Quantity) == "string", "Quantity is required!") 301 | assert(bint.__lt(0, bint(msg.Quantity)), "Quantity must be greater than 0") 302 | assert(type(msg.MemTarget) == "string", "Target is required!") 303 | 304 | local qty = bint(msg.Quantity) 305 | local balance = bint(Balances[msg.From]) 306 | if bint.__le(qty, balance) then 307 | Balances[msg.From] = tostring(bint.__sub(balance, qty)) 308 | 309 | BurnReqs[msg.Id] = { 310 | qty = tostring(qty), 311 | caller = msg.From, 312 | mem_target = msg.MemTarget 313 | } 314 | 315 | --[[ 316 | Only send the notifications to the Sender 317 | if the Cast tag is not set on the Burn message 318 | ]] 319 | -- 320 | if not msg.Cast then 321 | -- Send Burn-Notice to the Sender 322 | ao.send( 323 | { 324 | Target = msg.From, 325 | Action = "Burn-Notice", 326 | Quantity = tostring(qty), 327 | Data = Colors.gray .. "You Burned " .. Colors.blue .. msg.Quantity 328 | } 329 | ) 330 | end 331 | else 332 | ao.send( 333 | { 334 | Target = msg.From, 335 | Action = "Burn-Error", 336 | ["Message-Id"] = msg.Id, 337 | Error = "Insufficient Balance!" 338 | } 339 | ) 340 | end 341 | end 342 | ) 343 | -------------------------------------------------------------------------------- /contracts/evm/bridge.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | /** 5 | 6 | 7 | _____ _____ _____ _____ _____ _____ _____ _____ _____ 8 | /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ 9 | /::\____\ /::\ \ /::\____\ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ 10 | /::::| | /::::\ \ /::::| | /::::\ \ /::::\ \ \:::\ \ /::::\ \ /::::\ \ /::::\ \ 11 | /:::::| | /::::::\ \ /:::::| | /::::::\ \ /::::::\ \ \:::\ \ /::::::\ \ /::::::\ \ /::::::\ \ 12 | /::::::| | /:::/\:::\ \ /::::::| | /:::/\:::\ \ /:::/\:::\ \ \:::\ \ /:::/\:::\ \ /:::/\:::\ \ /:::/\:::\ \ 13 | /:::/|::| | /:::/__\:::\ \ /:::/|::| | /:::/__\:::\ \ /:::/__\:::\ \ \:::\ \ /:::/ \:::\ \ /:::/ \:::\ \ /:::/__\:::\ \ 14 | /:::/ |::| | /::::\ \:::\ \ /:::/ |::| | /::::\ \:::\ \ /::::\ \:::\ \ /::::\ \ /:::/ \:::\ \ /:::/ \:::\ \ /::::\ \:::\ \ 15 | /:::/ |::|___|______ /::::::\ \:::\ \ /:::/ |::|___|______ /::::::\ \:::\ \ /::::::\ \:::\ \ ____ /::::::\ \ /:::/ / \:::\ \ /:::/ / \:::\ \ /::::::\ \:::\ \ 16 | /:::/ |::::::::\ \ /:::/\:::\ \:::\ \ /:::/ |::::::::\ \ /:::/\:::\ \:::\ ___\ /:::/\:::\ \:::\____\ /\ \ /:::/\:::\ \ /:::/ / \:::\ ___\ /:::/ / \:::\ ___\ /:::/\:::\ \:::\ \ 17 | /:::/ |:::::::::\____\/:::/__\:::\ \:::\____\/:::/ |:::::::::\____\ /:::/__\:::\ \:::| |/:::/ \:::\ \:::| |/::\ \/:::/ \:::\____\/:::/____/ \:::| |/:::/____/ ___\:::| |/:::/__\:::\ \:::\____\ 18 | \::/ / ~~~~~/:::/ /\:::\ \:::\ \::/ /\::/ / ~~~~~/:::/ / \:::\ \:::\ /:::|____|\::/ |::::\ /:::|____|\:::\ /:::/ \::/ /\:::\ \ /:::|____|\:::\ \ /\ /:::|____|\:::\ \:::\ \::/ / 19 | \/____/ /:::/ / \:::\ \:::\ \/____/ \/____/ /:::/ / \:::\ \:::\/:::/ / \/____|:::::\/:::/ / \:::\/:::/ / \/____/ \:::\ \ /:::/ / \:::\ /::\ \::/ / \:::\ \:::\ \/____/ 20 | /:::/ / \:::\ \:::\ \ /:::/ / \:::\ \::::::/ / |:::::::::/ / \::::::/ / \:::\ \ /:::/ / \:::\ \:::\ \/____/ \:::\ \:::\ \ 21 | /:::/ / \:::\ \:::\____\ /:::/ / \:::\ \::::/ / |::|\::::/ / \::::/____/ \:::\ /:::/ / \:::\ \:::\____\ \:::\ \:::\____\ 22 | /:::/ / \:::\ \::/ / /:::/ / \:::\ /:::/ / |::| \::/____/ \:::\ \ \:::\ /:::/ / \:::\ /:::/ / \:::\ \::/ / 23 | /:::/ / \:::\ \/____/ /:::/ / \:::\/:::/ / |::| ~| \:::\ \ \:::\/:::/ / \:::\/:::/ / \:::\ \/____/ 24 | /:::/ / \:::\ \ /:::/ / \::::::/ / |::| | \:::\ \ \::::::/ / \::::::/ / \:::\ \ 25 | /:::/ / \:::\____\ /:::/ / \::::/ / \::| | \:::\____\ \::::/ / \::::/ / \:::\____\ 26 | \::/ / \::/ / \::/ / \::/____/ \:| | \::/ / \::/____/ \::/____/ \::/ / 27 | \/____/ \/____/ \/____/ ~~ \|___| \/____/ ~~ \/____/ 28 | 29 | 30 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 31 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#GPYJJ?????JJY5PGB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 32 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#GY?7!!!77777????JJJJYY5GB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 33 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&GJ7!!!!!!!!!!!777????JJJYYY55G#&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 34 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&P?!!!!!~~~~~~~!!!!777??JJJYYY555PPB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 35 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G?77!!!~~~~~~~~~~~!!!77???JJJYYY55PPPG#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 36 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@57777!!~~~~~~~~~~~~~!!777??JJJYYY55PPPGGB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 37 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Y??777!!~~~~~~~~~~~~!!!777??JJJYYY55PPPGGGB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 38 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@P???777!!!~~~~~~~~~~~!!!77???JJJYY555PPPGGBB#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 39 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#JJ???7777!!!!~~~~~!!!!777???JJJYYY555PPGGGBBB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 40 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@PJJJJ???7777!!!!!!!!!7777???JJJYYY555PPPGGGBBB#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 41 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5YYJJJ?????77777777777????JJJJYYY555PPPGGGBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 42 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5YYYYJJJJ???????????????JJJJYYYY555PPPGGGBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 43 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G555YYJJJ?????JJJJJJJJJJJYYYYY555PPPPGGGBBBBBB#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 44 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#55YJ???777777?JJJJJYYYYYYY5555PPPPGGGBBBBBB##&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 45 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&&&&&&&&@@@@@@@@@@@@@@@@@@@@@@@&#PJ??777777???JJJYYYYYY555555PPPPPGGGGBBBBBB###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 46 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&BP5YJ??????JJYY5PG#&@@@@@@@@@@@@@@#G5J?7777777??JJJYYY555555555PPPPPPGGGGBBBBBBB####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 47 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@B5J7!!!!!77777????JJJYY5PG#&@@@@@@&B5J?7777777???JJYYY555PPPPPPPPPPPPGGGGGGBBBBBBB#####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 48 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#5?!!!!!!!!!!!!7777???JJJYYY55PG&&BPY?7777777???JJJYY555PPPGGGGGGGGGGGGGGGBBBBBBBBB#####&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 49 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#57!!!!~~~~~~~~!!!!777???JJYYY555PPPJ7777777??JJJYYY555PPGGGBBBGGGGGGBBBBBBBBBBBBB#######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 50 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@P?77!!!~~~~~~~~~~~!!!77???JJJYYY55PPPG577???JJYYY555PPPGGBBBBBBBBBBBBBBBBBBBBBB########&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 51 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&Y7777!!~~~~~~~~~~~~~!!777??JJJYYY55PPPGGPJJJYY555PPPGGBB#&@@&##BBBBBBBBBBBB##########&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 52 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&Y??777!!~~~~~~~~~~~~!!!777??JJJYYY55PPPGGBG555PPPPGGB#&@@@@@@@@@@&&##############&&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 53 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5????77!!!~~~~~~~~~~!!!777???JJJYY555PPPGGBBGPPGGB#&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 54 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BJJJ???777!!!!~~~~!!!!!777???JJJYYY555PPGGGBBBGB#&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 55 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@PJJJJ???77777!!!!!!!77777???JJJYYY555PPPGGBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 56 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5YYJJJJ????77777777777????JJJJYYY555PPPGGGBBBBB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 57 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@PYYYYJJJJJ?????????????JJJJJYYY5555PPPGGGBBBBBB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 58 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G55YYYYYJJJJJJJJJJJJJJJJJYYYY5555PPPGGGGBBBBB#B@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 59 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&P5555YYYYYYYYJJJJYYYYYYYY55555PPPPGGGBBBBBB#B#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 60 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BPPP555555YYYYYYYYYY5555555PPPPGGGGBBBBBBB###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 61 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BPPPPPP555555555555555PPPPPPGGGGBBBBBBB####&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 62 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#GGGPPPPPPPPPPPPPPPPPPGGGGGGBBBBBBBB#####&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 63 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&BGGGGGGGGGGGGGGGGGGGGGBBBBBBBBBB######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 64 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&BBBBBBBGGGGGBBBBBBBBBBBBBBB#######&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 65 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#BBBBBBBBBBBBBBBBBBBB########&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 66 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&###BBBBBBBBB###########&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 67 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&#############&&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 68 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 69 | **/ 70 | 71 | // Imports 72 | import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; 73 | import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol"; 74 | import {Chainlink, ChainlinkClient} from "@chainlink/contracts/src/v0.8/ChainlinkClient.sol"; 75 | import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; 76 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 77 | 78 | /// @title MemBridge contract 79 | /// @notice crosschain ERC20 token bridge for MEM serverless functions 80 | /// @dev Inherits from ERC20 for token functionality, Ownable for ownership management, and ChainlinkClient for oracle 81 | /// @author charmful0x 82 | /// @custom:security-contact darwin@decent.land 83 | 84 | contract MemBridge is ChainlinkClient, ConfirmedOwner { 85 | using SafeERC20 for IERC20; 86 | using Chainlink for Chainlink.Request; 87 | 88 | IERC20 public immutable token; 89 | 90 | // Events declaration 91 | 92 | event Lock(address address_, uint256 amount_); 93 | event Unlock(address address_, uint256 amount_); 94 | event Request(bytes32 indexed requestId_, uint256 result_); 95 | 96 | // State variables declaration 97 | 98 | // chainlink jobId 99 | bytes32 private jobId; 100 | // chainlink oracle fee 101 | uint256 private oracleFee; 102 | // bridge in tokens amount 103 | uint256 private bridgeLockFee; 104 | // chainlink oracle address 105 | address private oracleAddress; 106 | // treasury EOA 107 | address private treasury; 108 | // validateUnlock EOA 109 | address private cronjobAddress; 110 | // min bridgeable amount 111 | uint256 private minBamount; 112 | // unlocking flat fee in token amount 113 | uint256 private unlockFlatFee; 114 | // the base endpoint of the MEM Bridge 115 | string baseEndpoint; 116 | // stats: accumulated fees 117 | uint256 public cumulativeFees; 118 | // stats: total locked ERC20s 119 | uint256 public totalLocked; 120 | 121 | // Maps declaration 122 | 123 | // locked ERC20 balances 124 | mapping(address => uint) public balanceOf; 125 | // unlocking requests amount (result) 126 | mapping(bytes32 => uint256) public requests; 127 | // mapping unlockIds to MEM IDs 128 | mapping(bytes32 => string) public reqToMemId; 129 | // mapping unlockIds to MEM IDs 130 | mapping(string => bytes32) public MemIdToReq; 131 | // mapping MEM ID to its redeeming status 132 | mapping(string => bool) public midIsRedeemed; 133 | // map requestId to caller 134 | mapping(bytes32 => address) public reqToCaller; 135 | 136 | /// @notice Constructor to initialize the MemBridge contract 137 | /// @param _btoken The address of the ERC20 token to bridge 138 | /// @param _oracleAddress The address of the chainlink node/oracle 139 | /// @param _linkTokenAddr The address of $LINK token on the contract deployed chain 140 | /// @param _treasuryAddr The address of the bridge's treasury that collects fees 141 | /// @param _cronjobAddr The address of the bridge's cronjob that validate unlocks 142 | /// @param _jobId The oracle jobId 143 | /// @param _ofee The oracle $LINK fee 144 | /// @param _bLockfee The bridge service fee in hundredths of a percent 145 | /// @param _minBAmount The minimal bridgeable amount of tokens 146 | /// @param _unlockFlatFee Unlocking locked token flat fee amount 147 | /// @param _baseEndpoint The MEM API base endpoint 148 | constructor( 149 | IERC20 _btoken, 150 | address _oracleAddress, 151 | address _linkTokenAddr, 152 | address _treasuryAddr, 153 | address _cronjobAddr, 154 | string memory _jobId, 155 | string memory _baseEndpoint, 156 | uint256 _ofee, 157 | uint256 _bLockfee, 158 | uint256 _minBAmount, 159 | uint256 _unlockFlatFee 160 | ) ConfirmedOwner(msg.sender) { 161 | address uinitialized = address(0); 162 | require( 163 | _oracleAddress != uinitialized && 164 | _linkTokenAddr != uinitialized && 165 | _treasuryAddr != uinitialized && 166 | _cronjobAddr != uinitialized && 167 | address(_btoken) != uinitialized 168 | ); 169 | token = _btoken; 170 | treasury = _treasuryAddr; 171 | cronjobAddress = _cronjobAddr; 172 | minBamount = _minBAmount; 173 | setChainlinkToken(_linkTokenAddr); 174 | setChainlinkOracle(_oracleAddress); 175 | setJobId(_jobId); 176 | setFeeInHundredthsOfLink(_ofee); 177 | bridgeLockFee = _bLockfee; 178 | unlockFlatFee = _unlockFlatFee; 179 | baseEndpoint = _baseEndpoint; 180 | } 181 | 182 | /// @notice The function that reads data from MEM part of the bridge 183 | /// @dev After issuing an unlock on MEM function, use the memid of that unlock req to fetch the unlockable amount 184 | /// This function send the request to the LinkWellNodes Chainlink's oracle and receive the amount that the user 185 | /// can unlock for a given mem id. 186 | /// @param _memid The mem id of the issued unlock on the MEM serverless function 187 | /// @param _caller The msg.sender EOA passed by the cronjob from MEM 188 | function validateUnlock( 189 | string calldata _memid, 190 | address _caller 191 | ) public returns (bytes32 requestId) { 192 | assert(msg.sender == cronjobAddress); 193 | // memid can be redeemed once 194 | assert(!midIsRedeemed[_memid]); 195 | // chainlink request 196 | Chainlink.Request memory req = buildOperatorRequest( 197 | jobId, 198 | this.fulfill.selector 199 | ); 200 | 201 | // construct the API req full URL 202 | string memory arg1 = string.concat(baseEndpoint, _memid); 203 | string memory arg2 = string.concat( 204 | "/", 205 | Strings.toHexString(uint256(uint160(_caller)), 20) 206 | ); 207 | string memory url = string.concat(arg1, arg2); 208 | 209 | // Set Chain req object 210 | req.add("method", "GET"); 211 | req.add("url", url); 212 | req.add("path", "amount"); 213 | req.add( 214 | "headers", 215 | '["content-type", "application/json", "set-cookie", "sid=14A52"]' 216 | ); 217 | req.add("body", ""); 218 | req.add("contact", "https://t.me/decentland"); 219 | req.addInt("multiplier", 1); // MEM store balances in BigInt as well 220 | 221 | // Sends the request 222 | requestId = sendOperatorRequest(req, oracleFee); 223 | // map requestId to _caller 224 | reqToCaller[requestId] = _caller; 225 | // map the chainlink requestId to memid 226 | reqToMemId[requestId] = _memid; 227 | // map the memid to chainlink requestId (read-only purposes only) 228 | // to retrieve the memid associated with a requestId, users should 229 | // use the reqToMemId map 230 | MemIdToReq[_memid] = requestId; 231 | return requestId; 232 | } 233 | /// @notice This function is called by the Chainlink oracle to resolve a request 234 | /// @dev The fulfill function is self-desriptive within the Chainlink usage context 235 | /// @param _requestId The oracle request ID 236 | /// @param _result The result of the requestId resolved by the oracle 237 | function fulfill( 238 | bytes32 _requestId, 239 | uint256 _result 240 | ) public recordChainlinkFulfillment(_requestId) returns (uint256) { 241 | string memory memid; 242 | // caller can't redeem memid with 0 amount 243 | require(_result > 0, "err_zero_amount"); 244 | // retrieve the memid using the requestId and check its redeeming status 245 | memid = reqToMemId[_requestId]; 246 | require(!midIsRedeemed[memid], "err_mid_redeemed"); 247 | // map the chainlink request result to the corresponding requestId 248 | requests[_requestId] = _result; 249 | emit Request(_requestId, _result); 250 | return _result; 251 | } 252 | 253 | /// @notice The lock function allows the users to lock the _btoken bond to this contract 254 | /// @dev Lock _btoken to the caller's address 255 | /// @param _amount The amount of tokens to lock 256 | /// @param _to An optional parameter to make the bridge compatible with smart wallets 257 | function lock(uint256 _amount, address _to) external { 258 | address caller; 259 | uint256 net_amount = computeNetAmount(_amount); 260 | uint256 generateFees = _amount - net_amount; 261 | // assign the correct EOA to _to param 262 | if (_to == address(0)) { 263 | caller = msg.sender; 264 | } else { 265 | caller = _to; 266 | } 267 | 268 | // ERC20 token transfer 269 | token.safeTransferFrom(msg.sender, address(this), _amount); 270 | // update balances map 271 | balanceOf[caller] += net_amount; 272 | // update treasury balance from fee cut 273 | balanceOf[treasury] += generateFees; 274 | // update totalLocked amount 275 | totalLocked += net_amount; 276 | //update treasury cumultive fee 277 | cumulativeFees += generateFees; 278 | // emit event 279 | emit Lock(caller, net_amount); 280 | } 281 | 282 | /// @notice This function is called after the validatUnlock() using the requestId of the memid 283 | /// @dev After calling validateUnlock() and mapping the requestId to amount, and requestId to memid, 284 | /// grab the requestId and call this function to finalize the TX lifecycle of a balance unlock action 285 | /// @param _requestId The requestId mapping the amount of tokens to unlock 286 | function executeUnlock(bytes32 _requestId) public { 287 | // retrieve request amount and mem id from maps 288 | uint256 amount = requests[_requestId]; 289 | require(amount > unlockFlatFee, "err_invalid_amount"); 290 | string memory memid = reqToMemId[_requestId]; 291 | // fee calculation 292 | uint256 net_amount = amount - unlockFlatFee; 293 | uint256 generateFees = amount - net_amount; 294 | // validate that the request owner is the function caller 295 | require(reqToCaller[_requestId] == msg.sender, "err_invalid_caller"); 296 | // do balances checks 297 | require( 298 | balanceOf[msg.sender] >= amount && balanceOf[msg.sender] > 0, 299 | "Insufficient funds" 300 | ); 301 | // seal this memid and make its reusage not possible 302 | midIsRedeemed[memid] = true; 303 | // update the caller balance 304 | balanceOf[msg.sender] -= amount; 305 | // update the treasury balance 306 | balanceOf[treasury] += generateFees; 307 | // update stats: cumulative fees 308 | cumulativeFees += generateFees; 309 | // update stats: total locked tokens 310 | totalLocked -= amount; 311 | //transfer the tokens 312 | token.safeTransfer(msg.sender, net_amount); 313 | // emit event 314 | emit Unlock(msg.sender, net_amount); 315 | } 316 | /// @notice Calculate the net amount upon locking 317 | /// @param _amount the lock amount (from requestId) 318 | function computeNetAmount(uint256 _amount) internal view returns (uint256) { 319 | uint256 bfee = (_amount * bridgeLockFee) / 10000; 320 | return _amount - bfee; 321 | } 322 | 323 | /// @notice Withdraw all of the $LINK token held by the contract (which is used to cover 324 | /// the oracle calls fees) 325 | /// @dev This function is called only by the contract owner 326 | function withdrawLink() public onlyOwner { 327 | LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress()); 328 | require( 329 | link.transfer(msg.sender, link.balanceOf(address(this))), 330 | "Unable to transfer" 331 | ); 332 | } 333 | 334 | /// @notice Withdraw the bridging service fees generated by contract usage 335 | /// to the treasury EOA. 336 | /// @dev Can be only called by the treasury EOA 337 | function withdrawFees() public { 338 | uint256 amount = balanceOf[treasury]; 339 | assert(amount > 0); 340 | require(msg.sender == treasury, "err_invalid_caller"); 341 | token.safeTransfer(treasury, amount); 342 | balanceOf[treasury] = 0; 343 | } 344 | 345 | /** 346 | Util Functions 347 | */ 348 | 349 | /// @notice Update oracle address 350 | /// @dev Can be only called by contract owner 351 | /// @param _oracleAddress The new oracle address 352 | function setOracleAddress(address _oracleAddress) public onlyOwner { 353 | require(_oracleAddress != address(0), "No address 0 allowed"); 354 | oracleAddress = _oracleAddress; 355 | setChainlinkOracle(_oracleAddress); 356 | } 357 | 358 | /// @notice retrieve currently in-use oracle address 359 | /// @dev Can be only called by contract owner 360 | function getOracleAddress() public view onlyOwner returns (address) { 361 | return oracleAddress; 362 | } 363 | 364 | /// @notice Update oracle's jobId 365 | /// @dev Can be only called by contract owner 366 | /// @param _jobId The jobId string identifier 367 | function setJobId(string memory _jobId) public onlyOwner { 368 | jobId = bytes32(bytes(_jobId)); 369 | } 370 | 371 | /// @notice Retrieve currently in-use jobId 372 | /// @dev Can be only called by contract owner 373 | function getJobId() public view onlyOwner returns (string memory) { 374 | return string(abi.encodePacked(jobId)); 375 | } 376 | 377 | /// @notice Update oracle's fee variable 378 | /// @dev Can be only called by contract owner 379 | /// @param _feeInJuels Fees in Juels 380 | function setFeeInJuels(uint256 _feeInJuels) public onlyOwner { 381 | oracleFee = _feeInJuels; 382 | } 383 | 384 | /// @notice Update oracle's fee variable 385 | /// @dev Can be only called by contract owner. This function 386 | /// is the main used function in the oracle's setup within 387 | /// this contract. 388 | /// @param _feeInHundredthsOfLink Fees in hundredth of $LINK (18 decimals) 389 | function setFeeInHundredthsOfLink( 390 | uint256 _feeInHundredthsOfLink 391 | ) public onlyOwner { 392 | setFeeInJuels((_feeInHundredthsOfLink * LINK_DIVISIBILITY) / 100); 393 | } 394 | 395 | /// @notice Get the oracleFee state variable fee value 396 | /// @dev Public GET function 397 | function getFeeInHundredthsOfLink() public view returns (uint256) { 398 | return (oracleFee * 100) / LINK_DIVISIBILITY; 399 | } 400 | 401 | /// @notice Update the MEM API base endpoint 402 | /// @dev Can be only called by contract owner 403 | /// @param _url New URL endpoint 404 | function setBaseEndpoint(string memory _url) public onlyOwner { 405 | baseEndpoint = _url; 406 | } 407 | 408 | /// @notice retrieve currently in-use base endpoint 409 | /// @dev Public GET function 410 | function getBaseEndpoint() public view returns (string memory) { 411 | return baseEndpoint; 412 | } 413 | 414 | /// @notice Update the MEM API base endpoint 415 | /// @dev Can be only called by contract owner 416 | /// @param _amount New URL endpoint 417 | function setBridgeLockFee(uint256 _amount) public onlyOwner { 418 | bridgeLockFee = _amount; 419 | } 420 | 421 | /// @notice retrieve currently in-use base endpoint 422 | /// @dev Public GET function 423 | function getBridgeLockFee() public view returns (uint256) { 424 | return bridgeLockFee; 425 | } 426 | 427 | /// @notice Update the minimum bridgeable amount 428 | /// @dev Can be only called by contract owner 429 | /// @param _amount New amount 430 | function setMinBamount(uint256 _amount) public onlyOwner { 431 | minBamount = _amount; 432 | } 433 | 434 | /// @notice retrieve currently in-use the minimum bridgeable amount 435 | /// @dev Public GET function 436 | function getMinBamount() public view returns (uint256) { 437 | return minBamount; 438 | } 439 | /// @notice Update the unlocking flat fee 440 | /// @dev Can be only called by contract owner 441 | /// @param _amount New amount 442 | function setUnlockFlatFee(uint256 _amount) public onlyOwner { 443 | unlockFlatFee = _amount; 444 | } 445 | 446 | /// @notice retrieve currently in-use the unlocking flat fee 447 | /// @dev Public GET function 448 | function getUnlockFlatFee() public view returns (uint256) { 449 | return unlockFlatFee; 450 | } 451 | } 452 | -------------------------------------------------------------------------------- /contracts/evm/bridge_usdc.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.25; 3 | 4 | /** 5 | 6 | 7 | _____ _____ _____ _____ _____ _____ _____ _____ _____ 8 | /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ /\ \ 9 | /::\____\ /::\ \ /::\____\ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ /::\ \ 10 | /::::| | /::::\ \ /::::| | /::::\ \ /::::\ \ \:::\ \ /::::\ \ /::::\ \ /::::\ \ 11 | /:::::| | /::::::\ \ /:::::| | /::::::\ \ /::::::\ \ \:::\ \ /::::::\ \ /::::::\ \ /::::::\ \ 12 | /::::::| | /:::/\:::\ \ /::::::| | /:::/\:::\ \ /:::/\:::\ \ \:::\ \ /:::/\:::\ \ /:::/\:::\ \ /:::/\:::\ \ 13 | /:::/|::| | /:::/__\:::\ \ /:::/|::| | /:::/__\:::\ \ /:::/__\:::\ \ \:::\ \ /:::/ \:::\ \ /:::/ \:::\ \ /:::/__\:::\ \ 14 | /:::/ |::| | /::::\ \:::\ \ /:::/ |::| | /::::\ \:::\ \ /::::\ \:::\ \ /::::\ \ /:::/ \:::\ \ /:::/ \:::\ \ /::::\ \:::\ \ 15 | /:::/ |::|___|______ /::::::\ \:::\ \ /:::/ |::|___|______ /::::::\ \:::\ \ /::::::\ \:::\ \ ____ /::::::\ \ /:::/ / \:::\ \ /:::/ / \:::\ \ /::::::\ \:::\ \ 16 | /:::/ |::::::::\ \ /:::/\:::\ \:::\ \ /:::/ |::::::::\ \ /:::/\:::\ \:::\ ___\ /:::/\:::\ \:::\____\ /\ \ /:::/\:::\ \ /:::/ / \:::\ ___\ /:::/ / \:::\ ___\ /:::/\:::\ \:::\ \ 17 | /:::/ |:::::::::\____\/:::/__\:::\ \:::\____\/:::/ |:::::::::\____\ /:::/__\:::\ \:::| |/:::/ \:::\ \:::| |/::\ \/:::/ \:::\____\/:::/____/ \:::| |/:::/____/ ___\:::| |/:::/__\:::\ \:::\____\ 18 | \::/ / ~~~~~/:::/ /\:::\ \:::\ \::/ /\::/ / ~~~~~/:::/ / \:::\ \:::\ /:::|____|\::/ |::::\ /:::|____|\:::\ /:::/ \::/ /\:::\ \ /:::|____|\:::\ \ /\ /:::|____|\:::\ \:::\ \::/ / 19 | \/____/ /:::/ / \:::\ \:::\ \/____/ \/____/ /:::/ / \:::\ \:::\/:::/ / \/____|:::::\/:::/ / \:::\/:::/ / \/____/ \:::\ \ /:::/ / \:::\ /::\ \::/ / \:::\ \:::\ \/____/ 20 | /:::/ / \:::\ \:::\ \ /:::/ / \:::\ \::::::/ / |:::::::::/ / \::::::/ / \:::\ \ /:::/ / \:::\ \:::\ \/____/ \:::\ \:::\ \ 21 | /:::/ / \:::\ \:::\____\ /:::/ / \:::\ \::::/ / |::|\::::/ / \::::/____/ \:::\ /:::/ / \:::\ \:::\____\ \:::\ \:::\____\ 22 | /:::/ / \:::\ \::/ / /:::/ / \:::\ /:::/ / |::| \::/____/ \:::\ \ \:::\ /:::/ / \:::\ /:::/ / \:::\ \::/ / 23 | /:::/ / \:::\ \/____/ /:::/ / \:::\/:::/ / |::| ~| \:::\ \ \:::\/:::/ / \:::\/:::/ / \:::\ \/____/ 24 | /:::/ / \:::\ \ /:::/ / \::::::/ / |::| | \:::\ \ \::::::/ / \::::::/ / \:::\ \ 25 | /:::/ / \:::\____\ /:::/ / \::::/ / \::| | \:::\____\ \::::/ / \::::/ / \:::\____\ 26 | \::/ / \::/ / \::/ / \::/____/ \:| | \::/ / \::/____/ \::/____/ \::/ / 27 | \/____/ \/____/ \/____/ ~~ \|___| \/____/ ~~ \/____/ 28 | 29 | 30 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 31 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#GPYJJ?????JJY5PGB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 32 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#GY?7!!!77777????JJJJYY5GB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 33 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&GJ7!!!!!!!!!!!777????JJJYYY55G#&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 34 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&P?!!!!!~~~~~~~!!!!777??JJJYYY555PPB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 35 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G?77!!!~~~~~~~~~~~!!!77???JJJYYY55PPPG#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 36 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@57777!!~~~~~~~~~~~~~!!777??JJJYYY55PPPGGB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 37 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@Y??777!!~~~~~~~~~~~~!!!777??JJJYYY55PPPGGGB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 38 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@P???777!!!~~~~~~~~~~~!!!77???JJJYY555PPPGGBB#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 39 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#JJ???7777!!!!~~~~~!!!!777???JJJYYY555PPGGGBBB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 40 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@PJJJJ???7777!!!!!!!!!7777???JJJYYY555PPPGGGBBB#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 41 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5YYJJJ?????77777777777????JJJJYYY555PPPGGGBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 42 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5YYYYJJJJ???????????????JJJJYYYY555PPPGGGBBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 43 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G555YYJJJ?????JJJJJJJJJJJYYYYY555PPPPGGGBBBBBB#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 44 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#55YJ???777777?JJJJJYYYYYYY5555PPPPGGGBBBBBB##&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 45 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&&&&&&&&@@@@@@@@@@@@@@@@@@@@@@@&#PJ??777777???JJJYYYYYY555555PPPPPGGGGBBBBBB###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 46 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&BP5YJ??????JJYY5PG#&@@@@@@@@@@@@@@#G5J?7777777??JJJYYY555555555PPPPPPGGGGBBBBBBB####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 47 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@B5J7!!!!!77777????JJJYY5PG#&@@@@@@&B5J?7777777???JJYYY555PPPPPPPPPPPPGGGGGGBBBBBBB#####@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 48 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#5?!!!!!!!!!!!!7777???JJJYYY55PG&&BPY?7777777???JJJYY555PPPGGGGGGGGGGGGGGGBBBBBBBBB#####&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 49 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#57!!!!~~~~~~~~!!!!777???JJYYY555PPPJ7777777??JJJYYY555PPGGGBBBGGGGGGBBBBBBBBBBBBB#######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 50 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@P?77!!!~~~~~~~~~~~!!!77???JJJYYY55PPPG577???JJYYY555PPPGGBBBBBBBBBBBBBBBBBBBBBB########&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 51 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&Y7777!!~~~~~~~~~~~~~!!777??JJJYYY55PPPGGPJJJYY555PPPGGBB#&@@&##BBBBBBBBBBBB##########&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 52 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&Y??777!!~~~~~~~~~~~~!!!777??JJJYYY55PPPGGBG555PPPPGGB#&@@@@@@@@@@&&##############&&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 53 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5????77!!!~~~~~~~~~~!!!777???JJJYY555PPPGGBBGPPGGB#&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 54 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BJJJ???777!!!!~~~~!!!!!777???JJJYYY555PPGGGBBBGB#&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 55 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@PJJJJ???77777!!!!!!!77777???JJJYYY555PPPGGBBBBB@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 56 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@5YYJJJJ????77777777777????JJJJYYY555PPPGGGBBBBB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 57 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@PYYYYJJJJJ?????????????JJJJJYYY5555PPPGGGBBBBBB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 58 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@G55YYYYYJJJJJJJJJJJJJJJJJYYYY5555PPPGGGGBBBBB#B@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 59 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&P5555YYYYYYYYJJJJYYYYYYYY55555PPPPGGGBBBBBB#B#@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 60 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BPPP555555YYYYYYYYYY5555555PPPPGGGGBBBBBBB###@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 61 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@BPPPPPP555555555555555PPPPPPGGGGBBBBBBB####&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 62 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@#GGGPPPPPPPPPPPPPPPPPPGGGGGGBBBBBBBB#####&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 63 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&BGGGGGGGGGGGGGGGGGGGGGBBBBBBBBBB######@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 64 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&BBBBBBBGGGGGBBBBBBBBBBBBBBB#######&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 65 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#BBBBBBBBBBBBBBBBBBBB########&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 66 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&###BBBBBBBBB###########&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 67 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&#############&&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 68 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 69 | **/ 70 | 71 | // Imports 72 | import {LinkTokenInterface} from "@chainlink/contracts/src/v0.8/shared/interfaces/LinkTokenInterface.sol"; 73 | import {ConfirmedOwner} from "@chainlink/contracts/src/v0.8/shared/access/ConfirmedOwner.sol"; 74 | import {Chainlink, ChainlinkClient} from "@chainlink/contracts/src/v0.8/ChainlinkClient.sol"; 75 | import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; 76 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 77 | 78 | /// @title MemBridge contract 79 | /// @notice crosschain ERC20 token bridge for MEM serverless functions 80 | /// @dev Inherits from ERC20 for token functionality, Ownable for ownership management, and ChainlinkClient for oracle 81 | /// @author charmful0x 82 | /// @custom:security-contact darwin@decent.land 83 | 84 | contract MemBridgeUsdc is ChainlinkClient, ConfirmedOwner { 85 | using SafeERC20 for IERC20; 86 | using Chainlink for Chainlink.Request; 87 | 88 | IERC20 public immutable token; 89 | 90 | // Events declaration 91 | 92 | event Lock(address address_, uint256 amount_); 93 | event Unlock(address address_, uint256 amount_); 94 | event Request(bytes32 indexed requestId_, uint256 result_); 95 | 96 | // State variables declaration 97 | 98 | // chainlink jobId 99 | bytes32 private jobId; 100 | // chainlink oracle fee 101 | uint256 private oracleFee; 102 | // bridge in tokens amount 103 | uint256 private bridgeLockFee; 104 | // chainlink oracle address 105 | address private oracleAddress; 106 | // treasury EOA 107 | address private treasury; 108 | // validateUnlock EOA 109 | address private cronjobAddress; 110 | // min bridgeable amount 111 | uint256 private minBamount; 112 | // unlocking flat fee in token amount 113 | uint256 private unlockFlatFee; 114 | // the base endpoint of the MEM Bridge 115 | string baseEndpoint; 116 | // stats: accumulated fees 117 | uint256 public cumulativeFees; 118 | // stats: total locked ERC20s 119 | uint256 public totalLocked; 120 | 121 | // Maps declaration 122 | 123 | // locked ERC20 balances 124 | mapping(address => uint) public balanceOf; 125 | // unlocking requests amount (result) 126 | mapping(bytes32 => uint256) public requests; 127 | // mapping unlockIds to MEM IDs 128 | mapping(bytes32 => string) public reqToMemId; 129 | // mapping unlockIds to MEM IDs 130 | mapping(string => bytes32) public MemIdToReq; 131 | // mapping MEM ID to its redeeming status 132 | mapping(string => bool) public midIsRedeemed; 133 | // map requestId to caller 134 | mapping(bytes32 => address) public reqToCaller; 135 | 136 | /// @notice Constructor to initialize the MemBridge contract 137 | /// @param _btoken The address of the ERC20 token to bridge 138 | /// @param _oracleAddress The address of the chainlink node/oracle 139 | /// @param _linkTokenAddr The address of $LINK token on the contract deployed chain 140 | /// @param _treasuryAddr The address of the bridge's treasury that collects fees 141 | /// @param _cronjobAddr The address of the bridge's cronjob that validate unlocks 142 | /// @param _jobId The oracle jobId 143 | /// @param _ofee The oracle $LINK fee 144 | /// @param _bLockfee The bridge service fee in hundredths of a percent 145 | /// @param _minBAmount The minimal bridgeable amount of tokens 146 | /// @param _unlockFlatFee Unlocking locked token flat fee amount 147 | /// @param _baseEndpoint The MEM API base endpoint 148 | constructor( 149 | IERC20 _btoken, 150 | address _oracleAddress, 151 | address _linkTokenAddr, 152 | address _treasuryAddr, 153 | address _cronjobAddr, 154 | string memory _jobId, 155 | string memory _baseEndpoint, 156 | uint256 _ofee, 157 | uint256 _bLockfee, 158 | uint256 _minBAmount, 159 | uint256 _unlockFlatFee 160 | ) ConfirmedOwner(msg.sender) { 161 | address uinitialized = address(0); 162 | require( 163 | _oracleAddress != uinitialized && 164 | _linkTokenAddr != uinitialized && 165 | _treasuryAddr != uinitialized && 166 | _cronjobAddr != uinitialized && 167 | address(_btoken) != uinitialized 168 | ); 169 | token = _btoken; 170 | treasury = _treasuryAddr; 171 | cronjobAddress = _cronjobAddr; 172 | minBamount = _minBAmount; 173 | _setChainlinkToken(_linkTokenAddr); 174 | _setChainlinkOracle(_oracleAddress); 175 | setJobId(_jobId); 176 | setFeeInHundredthsOfLink(_ofee); 177 | bridgeLockFee = _bLockfee; 178 | unlockFlatFee = _unlockFlatFee; 179 | baseEndpoint = _baseEndpoint; 180 | } 181 | 182 | /// @notice The function that reads data from MEM part of the bridge 183 | /// @dev After issuing an unlock on MEM function, use the memid of that unlock req to fetch the unlockable amount 184 | /// This function send the request to the LinkWellNodes Chainlink's oracle and receive the amount that the user 185 | /// can unlock for a given mem id. 186 | /// @param _memid The mem id of the issued unlock on the MEM serverless function 187 | /// @param _caller The msg.sender EOA passed by the cronjob from MEM 188 | function validateUnlock( 189 | string calldata _memid, 190 | address _caller 191 | ) public returns (bytes32 requestId) { 192 | assert(msg.sender == cronjobAddress); 193 | // memid can be redeemed once 194 | assert(!midIsRedeemed[_memid]); 195 | // chainlink request 196 | Chainlink.Request memory req = _buildOperatorRequest( 197 | jobId, 198 | this.fulfill.selector 199 | ); 200 | 201 | // construct the API req full URL 202 | string memory arg1 = string.concat(baseEndpoint, _memid); 203 | string memory arg2 = string.concat( 204 | "/", 205 | Strings.toHexString(uint256(uint160(_caller)), 20) 206 | ); 207 | string memory url = string.concat(arg1, arg2); 208 | 209 | // Set Chain req object 210 | req._add("method", "GET"); 211 | req._add("url", url); 212 | req._add("path", "amount"); 213 | req._add( 214 | "headers", 215 | '["content-type", "application/json", "set-cookie", "sid=14A52"]' 216 | ); 217 | req._add("body", ""); 218 | req._add("contact", "https://t.me/decentland"); 219 | req._addInt("multiplier", 1); // MEM store balances in BigInt as well 220 | 221 | // Sends the request 222 | requestId = _sendOperatorRequest(req, oracleFee); 223 | // map requestId to _caller 224 | reqToCaller[requestId] = _caller; 225 | // map the chainlink requestId to memid 226 | reqToMemId[requestId] = _memid; 227 | // map the memid to chainlink requestId (read-only purposes only) 228 | // to retrieve the memid associated with a requestId, users should 229 | // use the reqToMemId map 230 | MemIdToReq[_memid] = requestId; 231 | return requestId; 232 | } 233 | /// @notice This function is called by the Chainlink oracle to resolve a request 234 | /// @dev The fulfill function is self-desriptive within the Chainlink usage context 235 | /// @param _requestId The oracle request ID 236 | /// @param _result The result of the requestId resolved by the oracle 237 | function fulfill( 238 | bytes32 _requestId, 239 | uint256 _result 240 | ) public recordChainlinkFulfillment(_requestId) returns (uint256) { 241 | string memory memid; 242 | // caller can't redeem memid with 0 amount 243 | require(_result > 0, "err_zero_amount"); 244 | // retrieve the memid using the requestId and check its redeeming status 245 | memid = reqToMemId[_requestId]; 246 | require(!midIsRedeemed[memid], "err_mid_redeemed"); 247 | // map the chainlink request result to the corresponding requestId 248 | requests[_requestId] = _result; 249 | emit Request(_requestId, _result); 250 | return _result; 251 | } 252 | 253 | /// @notice The lock function allows the users to lock the _btoken bond to this contract 254 | /// @dev Lock _btoken to the caller's address 255 | /// @param _amount The amount of tokens to lock 256 | /// @param _to An optional parameter to make the bridge compatible with smart wallets 257 | function lock(uint256 _amount, address _to) external { 258 | address caller; 259 | uint256 net_amount = computeNetAmount(_amount); 260 | uint256 generateFees = _amount - net_amount; 261 | // assign the correct EOA to _to param 262 | if (_to == address(0)) { 263 | caller = msg.sender; 264 | } else { 265 | caller = _to; 266 | } 267 | 268 | // ERC20 token transfer 269 | token.safeTransferFrom(msg.sender, address(this), _amount); 270 | // update balances map 271 | balanceOf[caller] += net_amount; 272 | // update treasury balance from fee cut 273 | balanceOf[treasury] += generateFees; 274 | // update totalLocked amount 275 | totalLocked += net_amount; 276 | //update treasury cumultive fee 277 | cumulativeFees += generateFees; 278 | // emit event 279 | emit Lock(caller, net_amount); 280 | } 281 | 282 | /// @notice This function is called after the validatUnlock() using the requestId of the memid 283 | /// @dev After calling validateUnlock() and mapping the requestId to amount, and requestId to memid, 284 | /// grab the requestId and call this function to finalize the TX lifecycle of a balance unlock action 285 | /// @param _requestId The requestId mapping the amount of tokens to unlock 286 | function executeUnlock(bytes32 _requestId) public { 287 | // retrieve request amount and mem id from maps 288 | uint256 amount = requests[_requestId]; 289 | require(amount > unlockFlatFee, "err_invalid_amount"); 290 | string memory memid = reqToMemId[_requestId]; 291 | // fee calculation 292 | uint256 net_amount = amount - unlockFlatFee; 293 | uint256 generateFees = amount - net_amount; 294 | // validate that the request owner is the function caller 295 | require(reqToCaller[_requestId] == msg.sender, "err_invalid_caller"); 296 | // do balances checks 297 | require( 298 | balanceOf[msg.sender] >= amount && balanceOf[msg.sender] > 0, 299 | "Insufficient funds" 300 | ); 301 | // seal this memid and make its reusage not possible 302 | midIsRedeemed[memid] = true; 303 | // update the caller balance 304 | balanceOf[msg.sender] -= amount; 305 | // update the treasury balance 306 | balanceOf[treasury] += generateFees; 307 | // update stats: cumulative fees 308 | cumulativeFees += generateFees; 309 | // update stats: total locked tokens 310 | totalLocked -= amount; 311 | //transfer the tokens 312 | token.safeTransfer(msg.sender, net_amount); 313 | // emit event 314 | emit Unlock(msg.sender, net_amount); 315 | } 316 | /// @notice Calculate the net amount upon locking 317 | /// @param _amount the lock amount (from requestId) 318 | function computeNetAmount(uint256 _amount) internal view returns (uint256) { 319 | uint256 bfee = (_amount * bridgeLockFee) / 10000; 320 | return _amount - bfee; 321 | } 322 | 323 | /// @notice Withdraw all of the $LINK token held by the contract (which is used to cover 324 | /// the oracle calls fees) 325 | /// @dev This function is called only by the contract owner 326 | function withdrawLink() public onlyOwner { 327 | LinkTokenInterface link = LinkTokenInterface(_chainlinkTokenAddress()); 328 | require( 329 | link.transfer(msg.sender, link.balanceOf(address(this))), 330 | "Unable to transfer" 331 | ); 332 | } 333 | 334 | /// @notice Withdraw the bridging service fees generated by contract usage 335 | /// to the treasury EOA. 336 | /// @dev Can be only called by the treasury EOA 337 | function withdrawFees() public { 338 | uint256 amount = balanceOf[treasury]; 339 | assert(amount > 0); 340 | require(msg.sender == treasury, "err_invalid_caller"); 341 | token.safeTransfer(treasury, amount); 342 | balanceOf[treasury] = 0; 343 | } 344 | 345 | /** 346 | Util Functions 347 | */ 348 | 349 | /// @notice Update oracle address 350 | /// @dev Can be only called by contract owner 351 | /// @param _oracleAddress The new oracle address 352 | function setOracleAddress(address _oracleAddress) public onlyOwner { 353 | require(_oracleAddress != address(0), "No address 0 allowed"); 354 | oracleAddress = _oracleAddress; 355 | _setChainlinkOracle(_oracleAddress); 356 | } 357 | 358 | /// @notice retrieve currently in-use oracle address 359 | /// @dev Can be only called by contract owner 360 | function getOracleAddress() public view onlyOwner returns (address) { 361 | return oracleAddress; 362 | } 363 | 364 | /// @notice Update oracle's jobId 365 | /// @dev Can be only called by contract owner 366 | /// @param _jobId The jobId string identifier 367 | function setJobId(string memory _jobId) public onlyOwner { 368 | jobId = bytes32(bytes(_jobId)); 369 | } 370 | 371 | /// @notice Retrieve currently in-use jobId 372 | /// @dev Can be only called by contract owner 373 | function getJobId() public view onlyOwner returns (string memory) { 374 | return string(abi.encodePacked(jobId)); 375 | } 376 | 377 | /// @notice Update oracle's fee variable 378 | /// @dev Can be only called by contract owner 379 | /// @param _feeInJuels Fees in Juels 380 | function setFeeInJuels(uint256 _feeInJuels) public onlyOwner { 381 | oracleFee = _feeInJuels; 382 | } 383 | 384 | /// @notice Update oracle's fee variable 385 | /// @dev Can be only called by contract owner. This function 386 | /// is the main used function in the oracle's setup within 387 | /// this contract. 388 | /// @param _feeInHundredthsOfLink Fees in hundredth of $LINK (18 decimals) 389 | function setFeeInHundredthsOfLink( 390 | uint256 _feeInHundredthsOfLink 391 | ) public onlyOwner { 392 | setFeeInJuels((_feeInHundredthsOfLink * LINK_DIVISIBILITY) / 100); 393 | } 394 | 395 | /// @notice Get the oracleFee state variable fee value 396 | /// @dev Public GET function 397 | function getFeeInHundredthsOfLink() public view returns (uint256) { 398 | return (oracleFee * 100) / LINK_DIVISIBILITY; 399 | } 400 | 401 | /// @notice Update the MEM API base endpoint 402 | /// @dev Can be only called by contract owner 403 | /// @param _url New URL endpoint 404 | function setBaseEndpoint(string memory _url) public onlyOwner { 405 | baseEndpoint = _url; 406 | } 407 | 408 | /// @notice retrieve currently in-use base endpoint 409 | /// @dev Public GET function 410 | function getBaseEndpoint() public view returns (string memory) { 411 | return baseEndpoint; 412 | } 413 | 414 | /// @notice Update the MEM API base endpoint 415 | /// @dev Can be only called by contract owner 416 | /// @param _amount New URL endpoint 417 | function setBridgeLockFee(uint256 _amount) public onlyOwner { 418 | bridgeLockFee = _amount; 419 | } 420 | 421 | /// @notice retrieve currently in-use base endpoint 422 | /// @dev Public GET function 423 | function getBridgeLockFee() public view returns (uint256) { 424 | return bridgeLockFee; 425 | } 426 | 427 | /// @notice Update the minimum bridgeable amount 428 | /// @dev Can be only called by contract owner 429 | /// @param _amount New amount 430 | function setMinBamount(uint256 _amount) public onlyOwner { 431 | minBamount = _amount; 432 | } 433 | 434 | /// @notice retrieve currently in-use the minimum bridgeable amount 435 | /// @dev Public GET function 436 | function getMinBamount() public view returns (uint256) { 437 | return minBamount; 438 | } 439 | /// @notice Update the unlocking flat fee 440 | /// @dev Can be only called by contract owner 441 | /// @param _amount New amount 442 | function setUnlockFlatFee(uint256 _amount) public onlyOwner { 443 | unlockFlatFee = _amount; 444 | } 445 | 446 | /// @notice retrieve currently in-use the unlocking flat fee 447 | /// @dev Public GET function 448 | function getUnlockFlatFee() public view returns (uint256) { 449 | return unlockFlatFee; 450 | } 451 | } -------------------------------------------------------------------------------- /contracts/mem/bridge.js: -------------------------------------------------------------------------------- 1 | export async function handle(state, action) { 2 | const input = action.input; 3 | 4 | // lock on EVM, mint on MEM (EVM --> MEM) 5 | if (input.function === "executeLock") { 6 | // sig is not needed here as its already validated on EVM side 7 | // so without a sig,the dapp can invoke `executeLock`on behalf of user 8 | const { txid, caller } = input; 9 | const normalizedCaller = _normalizeCaller(caller); 10 | const req = ( 11 | await EXM.deterministicFetch( 12 | `${state.mem_molecule}/${txid}/${normalizedCaller}/${state.bridge_address}`, 13 | ) 14 | )?.asJSON(); 15 | ContractAssert(req.caller.toLowerCase() == normalizedCaller, "err"); 16 | ContractAssert(BigInt(req.amount) > 0n, "err"); 17 | ContractAssert( 18 | !state.locks.includes(txid.toLowerCase()), 19 | "err_lock_already_redeemed", 20 | ); 21 | state.locks.push(txid.toLowerCase()); 22 | 23 | if (!(normalizedCaller in state.balances)) { 24 | state.balances[normalizedCaller] = BigInt(0).toString(); 25 | } 26 | 27 | const newBalance = 28 | BigInt(state.balances[normalizedCaller]) + BigInt(req.amount); 29 | 30 | state.balances[normalizedCaller] = newBalance.toString(); 31 | 32 | return { state }; 33 | } 34 | // unlock on MEM, mint on EVM (MEM --> EVM) 35 | if (input.function === "initiateUnlock") { 36 | const { caller, sig, amount } = input; 37 | 38 | const bigIntAmount = BigInt(amount); 39 | 40 | ContractAssert(bigIntAmount > BigInt(state.evm_unlock_flatfee), "err_amount_too_low"); 41 | 42 | const normalizedCaller = _normalizeCaller(caller); 43 | ContractAssert( 44 | bigIntAmount <= BigInt(state.balances[normalizedCaller]), 45 | "err", 46 | ); 47 | 48 | await _moleculeSignatureVerification(normalizedCaller, sig); 49 | 50 | state.unlocks.push({ 51 | address: normalizedCaller, 52 | mid: sig, 53 | amount: amount, 54 | }); 55 | 56 | const newBalance = BigInt(state.balances[normalizedCaller]) - bigIntAmount; 57 | 58 | state.balances[normalizedCaller] = newBalance.toString(); 59 | 60 | return { state }; 61 | } 62 | 63 | // lock on MEM, mint on AO (MEM --> AO) 64 | if (input.function === "swapToAo") { 65 | const { caller, sig, ao_address, amount } = input; 66 | const normalizedCaller = _normalizeCaller(caller); 67 | const bigIntAmount = BigInt(amount); 68 | 69 | _validateArweaveAddress(ao_address); 70 | 71 | await _moleculeSignatureVerification(normalizedCaller, sig); 72 | 73 | ContractAssert(bigIntAmount > 0n, "err"); 74 | ContractAssert( 75 | BigInt(state.balances[normalizedCaller]) >= bigIntAmount, 76 | "err_invalid_amount", 77 | ); 78 | 79 | const newBalance = BigInt(state.balances[normalizedCaller]) - bigIntAmount; 80 | state.balances[normalizedCaller] = newBalance.toString(); 81 | 82 | state.aoLocks.push({ 83 | evm_caller: normalizedCaller, 84 | ao_address: ao_address, 85 | amount: bigIntAmount.toString(), 86 | id: sig, 87 | }); 88 | 89 | return { state }; 90 | } 91 | 92 | // lock on AO, mint on MEM (AO --> MEM) 93 | if (input.function === "executeUnlockFromAo") { 94 | const { caller, sig, auid } = input; 95 | 96 | ContractAssert( 97 | !state.aoUnlocks.includes(auid), 98 | "err_ao_unlock_id_already_used", 99 | ); 100 | 101 | const normalizedCaller = _normalizeCaller(caller); 102 | await _moleculeSignatureVerification(normalizedCaller, sig); 103 | const moleculeArg = btoa( 104 | JSON.stringify([{ name: "Action", value: "GetBurnReqs" }]), 105 | ); 106 | 107 | const aoUnlockIds = ( 108 | await EXM.deterministicFetch( 109 | `${state.ao_molecule_endpoint}/${state.ao_process_id}/${moleculeArg}`, 110 | ) 111 | )?.asJSON(); 112 | 113 | ContractAssert(auid in aoUnlockIds, "err_aouid_not_found"); 114 | 115 | const amount = BigInt(aoUnlockIds[auid].qty); 116 | const target = _normalizeCaller(aoUnlockIds[auid].mem_target); 117 | 118 | ContractAssert(target === normalizedCaller, "ERR_INVALID_CALLER"); 119 | 120 | state.aoUnlocks.push(auid); 121 | 122 | if (!(normalizedCaller in state.balances)) { 123 | state.balances[normalizedCaller] = BigInt(0).toString(); 124 | } 125 | 126 | const newBalance = BigInt(state.balances[normalizedCaller]) + amount; 127 | 128 | state.balances[normalizedCaller] = newBalance.toString(); 129 | 130 | return { state }; 131 | } 132 | // MEM <--> MEM 133 | if (input.function === "transfer") { 134 | const { caller, sig, target, amount } = input; 135 | 136 | const bigIntAmount = BigInt(amount); 137 | 138 | const normalizedCaller = _normalizeCaller(caller); 139 | const normalizedTarget = _normalizeCaller(target); 140 | 141 | ContractAssert( 142 | bigIntAmount <= BigInt(state.balances[normalizedCaller]), 143 | "err", 144 | ); 145 | ContractAssert(normalizedCaller !== normalizedTarget, "err_self_transfer"); 146 | 147 | await _moleculeSignatureVerification(normalizedCaller, sig); 148 | 149 | if (!(normalizedTarget in state.balances)) { 150 | state.balances[normalizedTarget] = BigInt(0n); 151 | } 152 | 153 | const newBalanceTarget = 154 | BigInt(state.balances[normalizedTarget]) + bigIntAmount; 155 | const newBalanceCaller = 156 | BigInt(state.balances[normalizedCaller]) - bigIntAmount; 157 | 158 | state.balances[normalizedTarget] = newBalanceTarget; 159 | state.balances[normalizedCaller] = newBalanceCaller; 160 | 161 | return { state }; 162 | } 163 | 164 | if (input.function === "updateAdminStateProperty") { 165 | const { caller, key, sig, value } = input; 166 | 167 | const updateAbleKeys = [ 168 | "bridge_address", 169 | "mem_molecule", 170 | "evm_molecule_endpoint", 171 | "ao_molecule_endpoint", 172 | "evm_unlock_flatfee", 173 | "sig_message", 174 | "ao_process_id", 175 | "name", 176 | "ticker" 177 | ]; 178 | 179 | const normalizedCaller = _normalizeCaller(caller); 180 | ContractAssert(normalizedCaller === state.admin, "ERROR_INVALID_CALLER"); 181 | ContractAssert(key in updateAbleKeys, "ERR_INVALID_UPDATEABLE_KEY"); 182 | ContractAssert( 183 | typeof value === "string" && value.length, 184 | "ERROR_INVALID_UPDATEABLE_VALUE", 185 | ); 186 | 187 | await _moleculeSignatureVerification(normalizedCaller, sig); 188 | 189 | state[key] = value; 190 | return { state }; 191 | } 192 | 193 | async function _moleculeSignatureVerification(caller, signature) { 194 | try { 195 | ContractAssert( 196 | !state.signatures.includes(signature), 197 | "ERROR_SIGNATURE_ALREADY_USED", 198 | ); 199 | 200 | const encodedMessage = btoa(`${state.sig_message}${state.counter}`); 201 | 202 | const isValid = await EXM.deterministicFetch( 203 | `${state.evm_molecule_endpoint}/signer/${caller}/${encodedMessage}/${signature}`, 204 | ); 205 | ContractAssert(isValid.asJSON()?.result, "ERROR_UNAUTHORIZED_CALLER"); 206 | state.signatures.push(signature); 207 | state.counter += 1; 208 | } catch (error) { 209 | throw new ContractError("ERROR_MOLECULE.SH_CONNECTION"); 210 | } 211 | } 212 | 213 | function _validateEoaSyntax(address) { 214 | ContractAssert( 215 | /^(0x)?[0-9a-fA-F]{40}$/.test(address), 216 | "ERROR_INVALID_EOA_ADDR", 217 | ); 218 | } 219 | 220 | function _validateArweaveAddress(address) { 221 | ContractAssert( 222 | /[a-z0-9_-]{43}/i.test(address), 223 | "ERROR_INVALID_ARWEAVE_ADDRESS", 224 | ); 225 | } 226 | 227 | function _normalizeCaller(address) { 228 | _validateEoaSyntax(address); 229 | return address.toLowerCase(); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /contracts/mem/bridge.json: -------------------------------------------------------------------------------- 1 | { 2 | "ticker": "mwUSDC", 3 | "name": "MEM Wrapped USDC", 4 | "precision": 6, 5 | "btoken_address": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", 6 | "bridge_address": "0x842b64bBA4D3bc5Cb29A7Bf73813a01CF684AF4a", 7 | "ao_process_id": "oDMJXlSOhJ6UjH5i7Dl-UOr_dhS1rQCX4r9ws0jvFps", 8 | "balances": {}, 9 | "mem_molecule": "https://0xmem.net/vl", 10 | "evm_molecule_endpoint": "http://evm.molecule.sh", 11 | "ao_molecule_endpoint": "https://molext1.com/ao/dryrundata", 12 | "counter": 0, 13 | "evm_unlock_flatfee": "2500000", 14 | "sig_message": "membridge-link::", 15 | "admin": "0x197f818c1313dc58b32d88078ecdfb40ea822614", 16 | "locks": [], 17 | "unlocks": [], 18 | "aoLocks": [], 19 | "aoUnlocks": [], 20 | "signatures": [] 21 | } 22 | -------------------------------------------------------------------------------- /img/lock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mem-fdn/mem-bridge/a1fac2cec9767e46be0ce65949216a48035fb3d0/img/lock.png -------------------------------------------------------------------------------- /img/unlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mem-fdn/mem-bridge/a1fac2cec9767e46be0ce65949216a48035fb3d0/img/unlock.png -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mem-bridge", 3 | "version": "0.1.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "mem-bridge", 9 | "version": "0.1.2", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@permaweb/aoconnect": "^0.0.48", 13 | "axios": "^1.6.7", 14 | "body-parser": "^1.20.2", 15 | "cors": "^2.8.5", 16 | "dotenv": "^16.4.5", 17 | "ethers": "^5.7.0", 18 | "express": "^4.18.3" 19 | }, 20 | "devDependencies": { 21 | "prettier": "^3.2.5", 22 | "prettier-plugin-solidity": "^1.3.1" 23 | } 24 | }, 25 | "node_modules/@ethersproject/abi": { 26 | "version": "5.7.0", 27 | "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", 28 | "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", 29 | "funding": [ 30 | { 31 | "type": "individual", 32 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 33 | }, 34 | { 35 | "type": "individual", 36 | "url": "https://www.buymeacoffee.com/ricmoo" 37 | } 38 | ], 39 | "dependencies": { 40 | "@ethersproject/address": "^5.7.0", 41 | "@ethersproject/bignumber": "^5.7.0", 42 | "@ethersproject/bytes": "^5.7.0", 43 | "@ethersproject/constants": "^5.7.0", 44 | "@ethersproject/hash": "^5.7.0", 45 | "@ethersproject/keccak256": "^5.7.0", 46 | "@ethersproject/logger": "^5.7.0", 47 | "@ethersproject/properties": "^5.7.0", 48 | "@ethersproject/strings": "^5.7.0" 49 | } 50 | }, 51 | "node_modules/@ethersproject/abstract-provider": { 52 | "version": "5.7.0", 53 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", 54 | "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", 55 | "funding": [ 56 | { 57 | "type": "individual", 58 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 59 | }, 60 | { 61 | "type": "individual", 62 | "url": "https://www.buymeacoffee.com/ricmoo" 63 | } 64 | ], 65 | "dependencies": { 66 | "@ethersproject/bignumber": "^5.7.0", 67 | "@ethersproject/bytes": "^5.7.0", 68 | "@ethersproject/logger": "^5.7.0", 69 | "@ethersproject/networks": "^5.7.0", 70 | "@ethersproject/properties": "^5.7.0", 71 | "@ethersproject/transactions": "^5.7.0", 72 | "@ethersproject/web": "^5.7.0" 73 | } 74 | }, 75 | "node_modules/@ethersproject/abstract-signer": { 76 | "version": "5.7.0", 77 | "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", 78 | "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", 79 | "funding": [ 80 | { 81 | "type": "individual", 82 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 83 | }, 84 | { 85 | "type": "individual", 86 | "url": "https://www.buymeacoffee.com/ricmoo" 87 | } 88 | ], 89 | "dependencies": { 90 | "@ethersproject/abstract-provider": "^5.7.0", 91 | "@ethersproject/bignumber": "^5.7.0", 92 | "@ethersproject/bytes": "^5.7.0", 93 | "@ethersproject/logger": "^5.7.0", 94 | "@ethersproject/properties": "^5.7.0" 95 | } 96 | }, 97 | "node_modules/@ethersproject/address": { 98 | "version": "5.7.0", 99 | "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", 100 | "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", 101 | "funding": [ 102 | { 103 | "type": "individual", 104 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 105 | }, 106 | { 107 | "type": "individual", 108 | "url": "https://www.buymeacoffee.com/ricmoo" 109 | } 110 | ], 111 | "dependencies": { 112 | "@ethersproject/bignumber": "^5.7.0", 113 | "@ethersproject/bytes": "^5.7.0", 114 | "@ethersproject/keccak256": "^5.7.0", 115 | "@ethersproject/logger": "^5.7.0", 116 | "@ethersproject/rlp": "^5.7.0" 117 | } 118 | }, 119 | "node_modules/@ethersproject/base64": { 120 | "version": "5.7.0", 121 | "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", 122 | "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", 123 | "funding": [ 124 | { 125 | "type": "individual", 126 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 127 | }, 128 | { 129 | "type": "individual", 130 | "url": "https://www.buymeacoffee.com/ricmoo" 131 | } 132 | ], 133 | "dependencies": { 134 | "@ethersproject/bytes": "^5.7.0" 135 | } 136 | }, 137 | "node_modules/@ethersproject/basex": { 138 | "version": "5.7.0", 139 | "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", 140 | "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", 141 | "funding": [ 142 | { 143 | "type": "individual", 144 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 145 | }, 146 | { 147 | "type": "individual", 148 | "url": "https://www.buymeacoffee.com/ricmoo" 149 | } 150 | ], 151 | "dependencies": { 152 | "@ethersproject/bytes": "^5.7.0", 153 | "@ethersproject/properties": "^5.7.0" 154 | } 155 | }, 156 | "node_modules/@ethersproject/bignumber": { 157 | "version": "5.7.0", 158 | "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", 159 | "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", 160 | "funding": [ 161 | { 162 | "type": "individual", 163 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 164 | }, 165 | { 166 | "type": "individual", 167 | "url": "https://www.buymeacoffee.com/ricmoo" 168 | } 169 | ], 170 | "dependencies": { 171 | "@ethersproject/bytes": "^5.7.0", 172 | "@ethersproject/logger": "^5.7.0", 173 | "bn.js": "^5.2.1" 174 | } 175 | }, 176 | "node_modules/@ethersproject/bytes": { 177 | "version": "5.7.0", 178 | "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", 179 | "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", 180 | "funding": [ 181 | { 182 | "type": "individual", 183 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 184 | }, 185 | { 186 | "type": "individual", 187 | "url": "https://www.buymeacoffee.com/ricmoo" 188 | } 189 | ], 190 | "dependencies": { 191 | "@ethersproject/logger": "^5.7.0" 192 | } 193 | }, 194 | "node_modules/@ethersproject/constants": { 195 | "version": "5.7.0", 196 | "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", 197 | "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", 198 | "funding": [ 199 | { 200 | "type": "individual", 201 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 202 | }, 203 | { 204 | "type": "individual", 205 | "url": "https://www.buymeacoffee.com/ricmoo" 206 | } 207 | ], 208 | "dependencies": { 209 | "@ethersproject/bignumber": "^5.7.0" 210 | } 211 | }, 212 | "node_modules/@ethersproject/contracts": { 213 | "version": "5.7.0", 214 | "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", 215 | "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", 216 | "funding": [ 217 | { 218 | "type": "individual", 219 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 220 | }, 221 | { 222 | "type": "individual", 223 | "url": "https://www.buymeacoffee.com/ricmoo" 224 | } 225 | ], 226 | "dependencies": { 227 | "@ethersproject/abi": "^5.7.0", 228 | "@ethersproject/abstract-provider": "^5.7.0", 229 | "@ethersproject/abstract-signer": "^5.7.0", 230 | "@ethersproject/address": "^5.7.0", 231 | "@ethersproject/bignumber": "^5.7.0", 232 | "@ethersproject/bytes": "^5.7.0", 233 | "@ethersproject/constants": "^5.7.0", 234 | "@ethersproject/logger": "^5.7.0", 235 | "@ethersproject/properties": "^5.7.0", 236 | "@ethersproject/transactions": "^5.7.0" 237 | } 238 | }, 239 | "node_modules/@ethersproject/hash": { 240 | "version": "5.7.0", 241 | "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", 242 | "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", 243 | "funding": [ 244 | { 245 | "type": "individual", 246 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 247 | }, 248 | { 249 | "type": "individual", 250 | "url": "https://www.buymeacoffee.com/ricmoo" 251 | } 252 | ], 253 | "dependencies": { 254 | "@ethersproject/abstract-signer": "^5.7.0", 255 | "@ethersproject/address": "^5.7.0", 256 | "@ethersproject/base64": "^5.7.0", 257 | "@ethersproject/bignumber": "^5.7.0", 258 | "@ethersproject/bytes": "^5.7.0", 259 | "@ethersproject/keccak256": "^5.7.0", 260 | "@ethersproject/logger": "^5.7.0", 261 | "@ethersproject/properties": "^5.7.0", 262 | "@ethersproject/strings": "^5.7.0" 263 | } 264 | }, 265 | "node_modules/@ethersproject/hdnode": { 266 | "version": "5.7.0", 267 | "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", 268 | "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", 269 | "funding": [ 270 | { 271 | "type": "individual", 272 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 273 | }, 274 | { 275 | "type": "individual", 276 | "url": "https://www.buymeacoffee.com/ricmoo" 277 | } 278 | ], 279 | "dependencies": { 280 | "@ethersproject/abstract-signer": "^5.7.0", 281 | "@ethersproject/basex": "^5.7.0", 282 | "@ethersproject/bignumber": "^5.7.0", 283 | "@ethersproject/bytes": "^5.7.0", 284 | "@ethersproject/logger": "^5.7.0", 285 | "@ethersproject/pbkdf2": "^5.7.0", 286 | "@ethersproject/properties": "^5.7.0", 287 | "@ethersproject/sha2": "^5.7.0", 288 | "@ethersproject/signing-key": "^5.7.0", 289 | "@ethersproject/strings": "^5.7.0", 290 | "@ethersproject/transactions": "^5.7.0", 291 | "@ethersproject/wordlists": "^5.7.0" 292 | } 293 | }, 294 | "node_modules/@ethersproject/json-wallets": { 295 | "version": "5.7.0", 296 | "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", 297 | "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", 298 | "funding": [ 299 | { 300 | "type": "individual", 301 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 302 | }, 303 | { 304 | "type": "individual", 305 | "url": "https://www.buymeacoffee.com/ricmoo" 306 | } 307 | ], 308 | "dependencies": { 309 | "@ethersproject/abstract-signer": "^5.7.0", 310 | "@ethersproject/address": "^5.7.0", 311 | "@ethersproject/bytes": "^5.7.0", 312 | "@ethersproject/hdnode": "^5.7.0", 313 | "@ethersproject/keccak256": "^5.7.0", 314 | "@ethersproject/logger": "^5.7.0", 315 | "@ethersproject/pbkdf2": "^5.7.0", 316 | "@ethersproject/properties": "^5.7.0", 317 | "@ethersproject/random": "^5.7.0", 318 | "@ethersproject/strings": "^5.7.0", 319 | "@ethersproject/transactions": "^5.7.0", 320 | "aes-js": "3.0.0", 321 | "scrypt-js": "3.0.1" 322 | } 323 | }, 324 | "node_modules/@ethersproject/keccak256": { 325 | "version": "5.7.0", 326 | "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", 327 | "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", 328 | "funding": [ 329 | { 330 | "type": "individual", 331 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 332 | }, 333 | { 334 | "type": "individual", 335 | "url": "https://www.buymeacoffee.com/ricmoo" 336 | } 337 | ], 338 | "dependencies": { 339 | "@ethersproject/bytes": "^5.7.0", 340 | "js-sha3": "0.8.0" 341 | } 342 | }, 343 | "node_modules/@ethersproject/logger": { 344 | "version": "5.7.0", 345 | "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", 346 | "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", 347 | "funding": [ 348 | { 349 | "type": "individual", 350 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 351 | }, 352 | { 353 | "type": "individual", 354 | "url": "https://www.buymeacoffee.com/ricmoo" 355 | } 356 | ] 357 | }, 358 | "node_modules/@ethersproject/networks": { 359 | "version": "5.7.0", 360 | "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.0.tgz", 361 | "integrity": "sha512-MG6oHSQHd4ebvJrleEQQ4HhVu8Ichr0RDYEfHzsVAVjHNM+w36x9wp9r+hf1JstMXtseXDtkiVoARAG6M959AA==", 362 | "funding": [ 363 | { 364 | "type": "individual", 365 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 366 | }, 367 | { 368 | "type": "individual", 369 | "url": "https://www.buymeacoffee.com/ricmoo" 370 | } 371 | ], 372 | "dependencies": { 373 | "@ethersproject/logger": "^5.7.0" 374 | } 375 | }, 376 | "node_modules/@ethersproject/pbkdf2": { 377 | "version": "5.7.0", 378 | "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", 379 | "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", 380 | "funding": [ 381 | { 382 | "type": "individual", 383 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 384 | }, 385 | { 386 | "type": "individual", 387 | "url": "https://www.buymeacoffee.com/ricmoo" 388 | } 389 | ], 390 | "dependencies": { 391 | "@ethersproject/bytes": "^5.7.0", 392 | "@ethersproject/sha2": "^5.7.0" 393 | } 394 | }, 395 | "node_modules/@ethersproject/properties": { 396 | "version": "5.7.0", 397 | "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", 398 | "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", 399 | "funding": [ 400 | { 401 | "type": "individual", 402 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 403 | }, 404 | { 405 | "type": "individual", 406 | "url": "https://www.buymeacoffee.com/ricmoo" 407 | } 408 | ], 409 | "dependencies": { 410 | "@ethersproject/logger": "^5.7.0" 411 | } 412 | }, 413 | "node_modules/@ethersproject/providers": { 414 | "version": "5.7.0", 415 | "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.0.tgz", 416 | "integrity": "sha512-+TTrrINMzZ0aXtlwO/95uhAggKm4USLm1PbeCBR/3XZ7+Oey+3pMyddzZEyRhizHpy1HXV0FRWRMI1O3EGYibA==", 417 | "funding": [ 418 | { 419 | "type": "individual", 420 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 421 | }, 422 | { 423 | "type": "individual", 424 | "url": "https://www.buymeacoffee.com/ricmoo" 425 | } 426 | ], 427 | "dependencies": { 428 | "@ethersproject/abstract-provider": "^5.7.0", 429 | "@ethersproject/abstract-signer": "^5.7.0", 430 | "@ethersproject/address": "^5.7.0", 431 | "@ethersproject/base64": "^5.7.0", 432 | "@ethersproject/basex": "^5.7.0", 433 | "@ethersproject/bignumber": "^5.7.0", 434 | "@ethersproject/bytes": "^5.7.0", 435 | "@ethersproject/constants": "^5.7.0", 436 | "@ethersproject/hash": "^5.7.0", 437 | "@ethersproject/logger": "^5.7.0", 438 | "@ethersproject/networks": "^5.7.0", 439 | "@ethersproject/properties": "^5.7.0", 440 | "@ethersproject/random": "^5.7.0", 441 | "@ethersproject/rlp": "^5.7.0", 442 | "@ethersproject/sha2": "^5.7.0", 443 | "@ethersproject/strings": "^5.7.0", 444 | "@ethersproject/transactions": "^5.7.0", 445 | "@ethersproject/web": "^5.7.0", 446 | "bech32": "1.1.4", 447 | "ws": "7.4.6" 448 | } 449 | }, 450 | "node_modules/@ethersproject/random": { 451 | "version": "5.7.0", 452 | "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", 453 | "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", 454 | "funding": [ 455 | { 456 | "type": "individual", 457 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 458 | }, 459 | { 460 | "type": "individual", 461 | "url": "https://www.buymeacoffee.com/ricmoo" 462 | } 463 | ], 464 | "dependencies": { 465 | "@ethersproject/bytes": "^5.7.0", 466 | "@ethersproject/logger": "^5.7.0" 467 | } 468 | }, 469 | "node_modules/@ethersproject/rlp": { 470 | "version": "5.7.0", 471 | "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", 472 | "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", 473 | "funding": [ 474 | { 475 | "type": "individual", 476 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 477 | }, 478 | { 479 | "type": "individual", 480 | "url": "https://www.buymeacoffee.com/ricmoo" 481 | } 482 | ], 483 | "dependencies": { 484 | "@ethersproject/bytes": "^5.7.0", 485 | "@ethersproject/logger": "^5.7.0" 486 | } 487 | }, 488 | "node_modules/@ethersproject/sha2": { 489 | "version": "5.7.0", 490 | "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", 491 | "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", 492 | "funding": [ 493 | { 494 | "type": "individual", 495 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 496 | }, 497 | { 498 | "type": "individual", 499 | "url": "https://www.buymeacoffee.com/ricmoo" 500 | } 501 | ], 502 | "dependencies": { 503 | "@ethersproject/bytes": "^5.7.0", 504 | "@ethersproject/logger": "^5.7.0", 505 | "hash.js": "1.1.7" 506 | } 507 | }, 508 | "node_modules/@ethersproject/signing-key": { 509 | "version": "5.7.0", 510 | "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", 511 | "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", 512 | "funding": [ 513 | { 514 | "type": "individual", 515 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 516 | }, 517 | { 518 | "type": "individual", 519 | "url": "https://www.buymeacoffee.com/ricmoo" 520 | } 521 | ], 522 | "dependencies": { 523 | "@ethersproject/bytes": "^5.7.0", 524 | "@ethersproject/logger": "^5.7.0", 525 | "@ethersproject/properties": "^5.7.0", 526 | "bn.js": "^5.2.1", 527 | "elliptic": "6.5.4", 528 | "hash.js": "1.1.7" 529 | } 530 | }, 531 | "node_modules/@ethersproject/solidity": { 532 | "version": "5.7.0", 533 | "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", 534 | "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", 535 | "funding": [ 536 | { 537 | "type": "individual", 538 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 539 | }, 540 | { 541 | "type": "individual", 542 | "url": "https://www.buymeacoffee.com/ricmoo" 543 | } 544 | ], 545 | "dependencies": { 546 | "@ethersproject/bignumber": "^5.7.0", 547 | "@ethersproject/bytes": "^5.7.0", 548 | "@ethersproject/keccak256": "^5.7.0", 549 | "@ethersproject/logger": "^5.7.0", 550 | "@ethersproject/sha2": "^5.7.0", 551 | "@ethersproject/strings": "^5.7.0" 552 | } 553 | }, 554 | "node_modules/@ethersproject/strings": { 555 | "version": "5.7.0", 556 | "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", 557 | "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", 558 | "funding": [ 559 | { 560 | "type": "individual", 561 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 562 | }, 563 | { 564 | "type": "individual", 565 | "url": "https://www.buymeacoffee.com/ricmoo" 566 | } 567 | ], 568 | "dependencies": { 569 | "@ethersproject/bytes": "^5.7.0", 570 | "@ethersproject/constants": "^5.7.0", 571 | "@ethersproject/logger": "^5.7.0" 572 | } 573 | }, 574 | "node_modules/@ethersproject/transactions": { 575 | "version": "5.7.0", 576 | "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", 577 | "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", 578 | "funding": [ 579 | { 580 | "type": "individual", 581 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 582 | }, 583 | { 584 | "type": "individual", 585 | "url": "https://www.buymeacoffee.com/ricmoo" 586 | } 587 | ], 588 | "dependencies": { 589 | "@ethersproject/address": "^5.7.0", 590 | "@ethersproject/bignumber": "^5.7.0", 591 | "@ethersproject/bytes": "^5.7.0", 592 | "@ethersproject/constants": "^5.7.0", 593 | "@ethersproject/keccak256": "^5.7.0", 594 | "@ethersproject/logger": "^5.7.0", 595 | "@ethersproject/properties": "^5.7.0", 596 | "@ethersproject/rlp": "^5.7.0", 597 | "@ethersproject/signing-key": "^5.7.0" 598 | } 599 | }, 600 | "node_modules/@ethersproject/units": { 601 | "version": "5.7.0", 602 | "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", 603 | "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", 604 | "funding": [ 605 | { 606 | "type": "individual", 607 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 608 | }, 609 | { 610 | "type": "individual", 611 | "url": "https://www.buymeacoffee.com/ricmoo" 612 | } 613 | ], 614 | "dependencies": { 615 | "@ethersproject/bignumber": "^5.7.0", 616 | "@ethersproject/constants": "^5.7.0", 617 | "@ethersproject/logger": "^5.7.0" 618 | } 619 | }, 620 | "node_modules/@ethersproject/wallet": { 621 | "version": "5.7.0", 622 | "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", 623 | "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", 624 | "funding": [ 625 | { 626 | "type": "individual", 627 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 628 | }, 629 | { 630 | "type": "individual", 631 | "url": "https://www.buymeacoffee.com/ricmoo" 632 | } 633 | ], 634 | "dependencies": { 635 | "@ethersproject/abstract-provider": "^5.7.0", 636 | "@ethersproject/abstract-signer": "^5.7.0", 637 | "@ethersproject/address": "^5.7.0", 638 | "@ethersproject/bignumber": "^5.7.0", 639 | "@ethersproject/bytes": "^5.7.0", 640 | "@ethersproject/hash": "^5.7.0", 641 | "@ethersproject/hdnode": "^5.7.0", 642 | "@ethersproject/json-wallets": "^5.7.0", 643 | "@ethersproject/keccak256": "^5.7.0", 644 | "@ethersproject/logger": "^5.7.0", 645 | "@ethersproject/properties": "^5.7.0", 646 | "@ethersproject/random": "^5.7.0", 647 | "@ethersproject/signing-key": "^5.7.0", 648 | "@ethersproject/transactions": "^5.7.0", 649 | "@ethersproject/wordlists": "^5.7.0" 650 | } 651 | }, 652 | "node_modules/@ethersproject/web": { 653 | "version": "5.7.0", 654 | "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.0.tgz", 655 | "integrity": "sha512-ApHcbbj+muRASVDSCl/tgxaH2LBkRMEYfLOLVa0COipx0+nlu0QKet7U2lEg0vdkh8XRSLf2nd1f1Uk9SrVSGA==", 656 | "funding": [ 657 | { 658 | "type": "individual", 659 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 660 | }, 661 | { 662 | "type": "individual", 663 | "url": "https://www.buymeacoffee.com/ricmoo" 664 | } 665 | ], 666 | "dependencies": { 667 | "@ethersproject/base64": "^5.7.0", 668 | "@ethersproject/bytes": "^5.7.0", 669 | "@ethersproject/logger": "^5.7.0", 670 | "@ethersproject/properties": "^5.7.0", 671 | "@ethersproject/strings": "^5.7.0" 672 | } 673 | }, 674 | "node_modules/@ethersproject/wordlists": { 675 | "version": "5.7.0", 676 | "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", 677 | "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", 678 | "funding": [ 679 | { 680 | "type": "individual", 681 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 682 | }, 683 | { 684 | "type": "individual", 685 | "url": "https://www.buymeacoffee.com/ricmoo" 686 | } 687 | ], 688 | "dependencies": { 689 | "@ethersproject/bytes": "^5.7.0", 690 | "@ethersproject/hash": "^5.7.0", 691 | "@ethersproject/logger": "^5.7.0", 692 | "@ethersproject/properties": "^5.7.0", 693 | "@ethersproject/strings": "^5.7.0" 694 | } 695 | }, 696 | "node_modules/@fastify/busboy": { 697 | "version": "2.1.1", 698 | "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", 699 | "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", 700 | "engines": { 701 | "node": ">=14" 702 | } 703 | }, 704 | "node_modules/@permaweb/ao-scheduler-utils": { 705 | "version": "0.0.15", 706 | "resolved": "https://registry.npmjs.org/@permaweb/ao-scheduler-utils/-/ao-scheduler-utils-0.0.15.tgz", 707 | "integrity": "sha512-Ef3IGP08E6KgcwX/AUq3k9gj7W/5GylEZRwXbXwC1JYl5FoHegdGAy0gDJYKHO1Nf4NVNFicb1xBaDeU0Ic7og==", 708 | "dependencies": { 709 | "lru-cache": "^10.2.0", 710 | "ramda": "^0.29.1" 711 | }, 712 | "engines": { 713 | "node": ">=18" 714 | } 715 | }, 716 | "node_modules/@permaweb/ao-scheduler-utils/node_modules/lru-cache": { 717 | "version": "10.2.0", 718 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", 719 | "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", 720 | "engines": { 721 | "node": "14 || >=16.14" 722 | } 723 | }, 724 | "node_modules/@permaweb/aoconnect": { 725 | "version": "0.0.48", 726 | "resolved": "https://registry.npmjs.org/@permaweb/aoconnect/-/aoconnect-0.0.48.tgz", 727 | "integrity": "sha512-CldwoE+6STKrfvknnmozRDBlo5gSHXLhmfAtAqHZvbMRprZ3yk+N3OIAt8La5axQ8Rwte5KKRcltvrNyxZpsVA==", 728 | "dependencies": { 729 | "@permaweb/ao-scheduler-utils": "~0.0.13", 730 | "buffer": "^6.0.3", 731 | "debug": "^4.3.4", 732 | "hyper-async": "^1.1.2", 733 | "mnemonist": "^0.39.8", 734 | "ramda": "^0.29.1", 735 | "warp-arbundles": "^1.0.4", 736 | "zod": "^3.22.4" 737 | }, 738 | "engines": { 739 | "node": ">=18" 740 | } 741 | }, 742 | "node_modules/@permaweb/aoconnect/node_modules/debug": { 743 | "version": "4.3.4", 744 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 745 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 746 | "dependencies": { 747 | "ms": "2.1.2" 748 | }, 749 | "engines": { 750 | "node": ">=6.0" 751 | }, 752 | "peerDependenciesMeta": { 753 | "supports-color": { 754 | "optional": true 755 | } 756 | } 757 | }, 758 | "node_modules/@permaweb/aoconnect/node_modules/ms": { 759 | "version": "2.1.2", 760 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 761 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 762 | }, 763 | "node_modules/@solidity-parser/parser": { 764 | "version": "0.17.0", 765 | "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.17.0.tgz", 766 | "integrity": "sha512-Nko8R0/kUo391jsEHHxrGM07QFdnPGvlmox4rmH0kNiNAashItAilhy4Mv4pK5gQmW5f4sXAF58fwJbmlkGcVw==", 767 | "dev": true 768 | }, 769 | "node_modules/accepts": { 770 | "version": "1.3.8", 771 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 772 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 773 | "dependencies": { 774 | "mime-types": "~2.1.34", 775 | "negotiator": "0.6.3" 776 | }, 777 | "engines": { 778 | "node": ">= 0.6" 779 | } 780 | }, 781 | "node_modules/aes-js": { 782 | "version": "3.0.0", 783 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", 784 | "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" 785 | }, 786 | "node_modules/arconnect": { 787 | "version": "0.4.2", 788 | "resolved": "https://registry.npmjs.org/arconnect/-/arconnect-0.4.2.tgz", 789 | "integrity": "sha512-Jkpd4QL3TVqnd3U683gzXmZUVqBUy17DdJDuL/3D9rkysLgX6ymJ2e+sR+xyZF5Rh42CBqDXWNMmCjBXeP7Gbw==", 790 | "dependencies": { 791 | "arweave": "^1.10.13" 792 | } 793 | }, 794 | "node_modules/array-flatten": { 795 | "version": "1.1.1", 796 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", 797 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" 798 | }, 799 | "node_modules/arweave": { 800 | "version": "1.15.0", 801 | "resolved": "https://registry.npmjs.org/arweave/-/arweave-1.15.0.tgz", 802 | "integrity": "sha512-sYfq2yJwkJLthRADsfHygNP+L7fTCyprTjOLYnpP8zaqwywddoNO3UpTk6XGjEiyyU3WfxoFLRLpzx+llZx1WA==", 803 | "dependencies": { 804 | "arconnect": "^0.4.2", 805 | "asn1.js": "^5.4.1", 806 | "base64-js": "^1.5.1", 807 | "bignumber.js": "^9.0.2" 808 | }, 809 | "engines": { 810 | "node": ">=18" 811 | } 812 | }, 813 | "node_modules/asn1.js": { 814 | "version": "5.4.1", 815 | "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", 816 | "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", 817 | "dependencies": { 818 | "bn.js": "^4.0.0", 819 | "inherits": "^2.0.1", 820 | "minimalistic-assert": "^1.0.0", 821 | "safer-buffer": "^2.1.0" 822 | } 823 | }, 824 | "node_modules/asn1.js/node_modules/bn.js": { 825 | "version": "4.12.0", 826 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", 827 | "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 828 | }, 829 | "node_modules/asynckit": { 830 | "version": "0.4.0", 831 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 832 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" 833 | }, 834 | "node_modules/axios": { 835 | "version": "1.6.7", 836 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", 837 | "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", 838 | "dependencies": { 839 | "follow-redirects": "^1.15.4", 840 | "form-data": "^4.0.0", 841 | "proxy-from-env": "^1.1.0" 842 | } 843 | }, 844 | "node_modules/base64-js": { 845 | "version": "1.5.1", 846 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 847 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 848 | "funding": [ 849 | { 850 | "type": "github", 851 | "url": "https://github.com/sponsors/feross" 852 | }, 853 | { 854 | "type": "patreon", 855 | "url": "https://www.patreon.com/feross" 856 | }, 857 | { 858 | "type": "consulting", 859 | "url": "https://feross.org/support" 860 | } 861 | ] 862 | }, 863 | "node_modules/base64url": { 864 | "version": "3.0.1", 865 | "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", 866 | "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", 867 | "engines": { 868 | "node": ">=6.0.0" 869 | } 870 | }, 871 | "node_modules/bech32": { 872 | "version": "1.1.4", 873 | "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", 874 | "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" 875 | }, 876 | "node_modules/bignumber.js": { 877 | "version": "9.1.2", 878 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", 879 | "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", 880 | "engines": { 881 | "node": "*" 882 | } 883 | }, 884 | "node_modules/bn.js": { 885 | "version": "5.2.1", 886 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", 887 | "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" 888 | }, 889 | "node_modules/body-parser": { 890 | "version": "1.20.2", 891 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", 892 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", 893 | "dependencies": { 894 | "bytes": "3.1.2", 895 | "content-type": "~1.0.5", 896 | "debug": "2.6.9", 897 | "depd": "2.0.0", 898 | "destroy": "1.2.0", 899 | "http-errors": "2.0.0", 900 | "iconv-lite": "0.4.24", 901 | "on-finished": "2.4.1", 902 | "qs": "6.11.0", 903 | "raw-body": "2.5.2", 904 | "type-is": "~1.6.18", 905 | "unpipe": "1.0.0" 906 | }, 907 | "engines": { 908 | "node": ">= 0.8", 909 | "npm": "1.2.8000 || >= 1.4.16" 910 | } 911 | }, 912 | "node_modules/brorand": { 913 | "version": "1.1.0", 914 | "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", 915 | "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" 916 | }, 917 | "node_modules/buffer": { 918 | "version": "6.0.3", 919 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", 920 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", 921 | "funding": [ 922 | { 923 | "type": "github", 924 | "url": "https://github.com/sponsors/feross" 925 | }, 926 | { 927 | "type": "patreon", 928 | "url": "https://www.patreon.com/feross" 929 | }, 930 | { 931 | "type": "consulting", 932 | "url": "https://feross.org/support" 933 | } 934 | ], 935 | "dependencies": { 936 | "base64-js": "^1.3.1", 937 | "ieee754": "^1.2.1" 938 | } 939 | }, 940 | "node_modules/bytes": { 941 | "version": "3.1.2", 942 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 943 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 944 | "engines": { 945 | "node": ">= 0.8" 946 | } 947 | }, 948 | "node_modules/call-bind": { 949 | "version": "1.0.7", 950 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", 951 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", 952 | "dependencies": { 953 | "es-define-property": "^1.0.0", 954 | "es-errors": "^1.3.0", 955 | "function-bind": "^1.1.2", 956 | "get-intrinsic": "^1.2.4", 957 | "set-function-length": "^1.2.1" 958 | }, 959 | "engines": { 960 | "node": ">= 0.4" 961 | }, 962 | "funding": { 963 | "url": "https://github.com/sponsors/ljharb" 964 | } 965 | }, 966 | "node_modules/combined-stream": { 967 | "version": "1.0.8", 968 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 969 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 970 | "dependencies": { 971 | "delayed-stream": "~1.0.0" 972 | }, 973 | "engines": { 974 | "node": ">= 0.8" 975 | } 976 | }, 977 | "node_modules/content-disposition": { 978 | "version": "0.5.4", 979 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", 980 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", 981 | "dependencies": { 982 | "safe-buffer": "5.2.1" 983 | }, 984 | "engines": { 985 | "node": ">= 0.6" 986 | } 987 | }, 988 | "node_modules/content-type": { 989 | "version": "1.0.5", 990 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 991 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 992 | "engines": { 993 | "node": ">= 0.6" 994 | } 995 | }, 996 | "node_modules/cookie": { 997 | "version": "0.5.0", 998 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", 999 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", 1000 | "engines": { 1001 | "node": ">= 0.6" 1002 | } 1003 | }, 1004 | "node_modules/cookie-signature": { 1005 | "version": "1.0.6", 1006 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", 1007 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" 1008 | }, 1009 | "node_modules/cors": { 1010 | "version": "2.8.5", 1011 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 1012 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 1013 | "dependencies": { 1014 | "object-assign": "^4", 1015 | "vary": "^1" 1016 | }, 1017 | "engines": { 1018 | "node": ">= 0.10" 1019 | } 1020 | }, 1021 | "node_modules/debug": { 1022 | "version": "2.6.9", 1023 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1024 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1025 | "dependencies": { 1026 | "ms": "2.0.0" 1027 | } 1028 | }, 1029 | "node_modules/define-data-property": { 1030 | "version": "1.1.4", 1031 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", 1032 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", 1033 | "dependencies": { 1034 | "es-define-property": "^1.0.0", 1035 | "es-errors": "^1.3.0", 1036 | "gopd": "^1.0.1" 1037 | }, 1038 | "engines": { 1039 | "node": ">= 0.4" 1040 | }, 1041 | "funding": { 1042 | "url": "https://github.com/sponsors/ljharb" 1043 | } 1044 | }, 1045 | "node_modules/delayed-stream": { 1046 | "version": "1.0.0", 1047 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 1048 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 1049 | "engines": { 1050 | "node": ">=0.4.0" 1051 | } 1052 | }, 1053 | "node_modules/depd": { 1054 | "version": "2.0.0", 1055 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 1056 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 1057 | "engines": { 1058 | "node": ">= 0.8" 1059 | } 1060 | }, 1061 | "node_modules/destroy": { 1062 | "version": "1.2.0", 1063 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", 1064 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", 1065 | "engines": { 1066 | "node": ">= 0.8", 1067 | "npm": "1.2.8000 || >= 1.4.16" 1068 | } 1069 | }, 1070 | "node_modules/dotenv": { 1071 | "version": "16.4.5", 1072 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", 1073 | "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", 1074 | "engines": { 1075 | "node": ">=12" 1076 | }, 1077 | "funding": { 1078 | "url": "https://dotenvx.com" 1079 | } 1080 | }, 1081 | "node_modules/ee-first": { 1082 | "version": "1.1.1", 1083 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 1084 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" 1085 | }, 1086 | "node_modules/elliptic": { 1087 | "version": "6.5.4", 1088 | "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", 1089 | "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", 1090 | "dependencies": { 1091 | "bn.js": "^4.11.9", 1092 | "brorand": "^1.1.0", 1093 | "hash.js": "^1.0.0", 1094 | "hmac-drbg": "^1.0.1", 1095 | "inherits": "^2.0.4", 1096 | "minimalistic-assert": "^1.0.1", 1097 | "minimalistic-crypto-utils": "^1.0.1" 1098 | } 1099 | }, 1100 | "node_modules/elliptic/node_modules/bn.js": { 1101 | "version": "4.12.0", 1102 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", 1103 | "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" 1104 | }, 1105 | "node_modules/encodeurl": { 1106 | "version": "1.0.2", 1107 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 1108 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", 1109 | "engines": { 1110 | "node": ">= 0.8" 1111 | } 1112 | }, 1113 | "node_modules/es-define-property": { 1114 | "version": "1.0.0", 1115 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", 1116 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", 1117 | "dependencies": { 1118 | "get-intrinsic": "^1.2.4" 1119 | }, 1120 | "engines": { 1121 | "node": ">= 0.4" 1122 | } 1123 | }, 1124 | "node_modules/es-errors": { 1125 | "version": "1.3.0", 1126 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 1127 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 1128 | "engines": { 1129 | "node": ">= 0.4" 1130 | } 1131 | }, 1132 | "node_modules/escape-html": { 1133 | "version": "1.0.3", 1134 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 1135 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" 1136 | }, 1137 | "node_modules/etag": { 1138 | "version": "1.8.1", 1139 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 1140 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 1141 | "engines": { 1142 | "node": ">= 0.6" 1143 | } 1144 | }, 1145 | "node_modules/ethers": { 1146 | "version": "5.7.0", 1147 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.0.tgz", 1148 | "integrity": "sha512-5Xhzp2ZQRi0Em+0OkOcRHxPzCfoBfgtOQA+RUylSkuHbhTEaQklnYi2hsWbRgs3ztJsXVXd9VKBcO1ScWL8YfA==", 1149 | "funding": [ 1150 | { 1151 | "type": "individual", 1152 | "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" 1153 | }, 1154 | { 1155 | "type": "individual", 1156 | "url": "https://www.buymeacoffee.com/ricmoo" 1157 | } 1158 | ], 1159 | "dependencies": { 1160 | "@ethersproject/abi": "5.7.0", 1161 | "@ethersproject/abstract-provider": "5.7.0", 1162 | "@ethersproject/abstract-signer": "5.7.0", 1163 | "@ethersproject/address": "5.7.0", 1164 | "@ethersproject/base64": "5.7.0", 1165 | "@ethersproject/basex": "5.7.0", 1166 | "@ethersproject/bignumber": "5.7.0", 1167 | "@ethersproject/bytes": "5.7.0", 1168 | "@ethersproject/constants": "5.7.0", 1169 | "@ethersproject/contracts": "5.7.0", 1170 | "@ethersproject/hash": "5.7.0", 1171 | "@ethersproject/hdnode": "5.7.0", 1172 | "@ethersproject/json-wallets": "5.7.0", 1173 | "@ethersproject/keccak256": "5.7.0", 1174 | "@ethersproject/logger": "5.7.0", 1175 | "@ethersproject/networks": "5.7.0", 1176 | "@ethersproject/pbkdf2": "5.7.0", 1177 | "@ethersproject/properties": "5.7.0", 1178 | "@ethersproject/providers": "5.7.0", 1179 | "@ethersproject/random": "5.7.0", 1180 | "@ethersproject/rlp": "5.7.0", 1181 | "@ethersproject/sha2": "5.7.0", 1182 | "@ethersproject/signing-key": "5.7.0", 1183 | "@ethersproject/solidity": "5.7.0", 1184 | "@ethersproject/strings": "5.7.0", 1185 | "@ethersproject/transactions": "5.7.0", 1186 | "@ethersproject/units": "5.7.0", 1187 | "@ethersproject/wallet": "5.7.0", 1188 | "@ethersproject/web": "5.7.0", 1189 | "@ethersproject/wordlists": "5.7.0" 1190 | } 1191 | }, 1192 | "node_modules/express": { 1193 | "version": "4.18.3", 1194 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", 1195 | "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", 1196 | "dependencies": { 1197 | "accepts": "~1.3.8", 1198 | "array-flatten": "1.1.1", 1199 | "body-parser": "1.20.2", 1200 | "content-disposition": "0.5.4", 1201 | "content-type": "~1.0.4", 1202 | "cookie": "0.5.0", 1203 | "cookie-signature": "1.0.6", 1204 | "debug": "2.6.9", 1205 | "depd": "2.0.0", 1206 | "encodeurl": "~1.0.2", 1207 | "escape-html": "~1.0.3", 1208 | "etag": "~1.8.1", 1209 | "finalhandler": "1.2.0", 1210 | "fresh": "0.5.2", 1211 | "http-errors": "2.0.0", 1212 | "merge-descriptors": "1.0.1", 1213 | "methods": "~1.1.2", 1214 | "on-finished": "2.4.1", 1215 | "parseurl": "~1.3.3", 1216 | "path-to-regexp": "0.1.7", 1217 | "proxy-addr": "~2.0.7", 1218 | "qs": "6.11.0", 1219 | "range-parser": "~1.2.1", 1220 | "safe-buffer": "5.2.1", 1221 | "send": "0.18.0", 1222 | "serve-static": "1.15.0", 1223 | "setprototypeof": "1.2.0", 1224 | "statuses": "2.0.1", 1225 | "type-is": "~1.6.18", 1226 | "utils-merge": "1.0.1", 1227 | "vary": "~1.1.2" 1228 | }, 1229 | "engines": { 1230 | "node": ">= 0.10.0" 1231 | } 1232 | }, 1233 | "node_modules/finalhandler": { 1234 | "version": "1.2.0", 1235 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", 1236 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", 1237 | "dependencies": { 1238 | "debug": "2.6.9", 1239 | "encodeurl": "~1.0.2", 1240 | "escape-html": "~1.0.3", 1241 | "on-finished": "2.4.1", 1242 | "parseurl": "~1.3.3", 1243 | "statuses": "2.0.1", 1244 | "unpipe": "~1.0.0" 1245 | }, 1246 | "engines": { 1247 | "node": ">= 0.8" 1248 | } 1249 | }, 1250 | "node_modules/follow-redirects": { 1251 | "version": "1.15.5", 1252 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", 1253 | "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", 1254 | "funding": [ 1255 | { 1256 | "type": "individual", 1257 | "url": "https://github.com/sponsors/RubenVerborgh" 1258 | } 1259 | ], 1260 | "engines": { 1261 | "node": ">=4.0" 1262 | }, 1263 | "peerDependenciesMeta": { 1264 | "debug": { 1265 | "optional": true 1266 | } 1267 | } 1268 | }, 1269 | "node_modules/form-data": { 1270 | "version": "4.0.0", 1271 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", 1272 | "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", 1273 | "dependencies": { 1274 | "asynckit": "^0.4.0", 1275 | "combined-stream": "^1.0.8", 1276 | "mime-types": "^2.1.12" 1277 | }, 1278 | "engines": { 1279 | "node": ">= 6" 1280 | } 1281 | }, 1282 | "node_modules/forwarded": { 1283 | "version": "0.2.0", 1284 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 1285 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 1286 | "engines": { 1287 | "node": ">= 0.6" 1288 | } 1289 | }, 1290 | "node_modules/fresh": { 1291 | "version": "0.5.2", 1292 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 1293 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", 1294 | "engines": { 1295 | "node": ">= 0.6" 1296 | } 1297 | }, 1298 | "node_modules/function-bind": { 1299 | "version": "1.1.2", 1300 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 1301 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 1302 | "funding": { 1303 | "url": "https://github.com/sponsors/ljharb" 1304 | } 1305 | }, 1306 | "node_modules/get-intrinsic": { 1307 | "version": "1.2.4", 1308 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", 1309 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", 1310 | "dependencies": { 1311 | "es-errors": "^1.3.0", 1312 | "function-bind": "^1.1.2", 1313 | "has-proto": "^1.0.1", 1314 | "has-symbols": "^1.0.3", 1315 | "hasown": "^2.0.0" 1316 | }, 1317 | "engines": { 1318 | "node": ">= 0.4" 1319 | }, 1320 | "funding": { 1321 | "url": "https://github.com/sponsors/ljharb" 1322 | } 1323 | }, 1324 | "node_modules/gopd": { 1325 | "version": "1.0.1", 1326 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", 1327 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", 1328 | "dependencies": { 1329 | "get-intrinsic": "^1.1.3" 1330 | }, 1331 | "funding": { 1332 | "url": "https://github.com/sponsors/ljharb" 1333 | } 1334 | }, 1335 | "node_modules/has-property-descriptors": { 1336 | "version": "1.0.2", 1337 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", 1338 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", 1339 | "dependencies": { 1340 | "es-define-property": "^1.0.0" 1341 | }, 1342 | "funding": { 1343 | "url": "https://github.com/sponsors/ljharb" 1344 | } 1345 | }, 1346 | "node_modules/has-proto": { 1347 | "version": "1.0.3", 1348 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", 1349 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", 1350 | "engines": { 1351 | "node": ">= 0.4" 1352 | }, 1353 | "funding": { 1354 | "url": "https://github.com/sponsors/ljharb" 1355 | } 1356 | }, 1357 | "node_modules/has-symbols": { 1358 | "version": "1.0.3", 1359 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", 1360 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", 1361 | "engines": { 1362 | "node": ">= 0.4" 1363 | }, 1364 | "funding": { 1365 | "url": "https://github.com/sponsors/ljharb" 1366 | } 1367 | }, 1368 | "node_modules/hash.js": { 1369 | "version": "1.1.7", 1370 | "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", 1371 | "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", 1372 | "dependencies": { 1373 | "inherits": "^2.0.3", 1374 | "minimalistic-assert": "^1.0.1" 1375 | } 1376 | }, 1377 | "node_modules/hasown": { 1378 | "version": "2.0.1", 1379 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", 1380 | "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", 1381 | "dependencies": { 1382 | "function-bind": "^1.1.2" 1383 | }, 1384 | "engines": { 1385 | "node": ">= 0.4" 1386 | } 1387 | }, 1388 | "node_modules/hmac-drbg": { 1389 | "version": "1.0.1", 1390 | "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", 1391 | "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", 1392 | "dependencies": { 1393 | "hash.js": "^1.0.3", 1394 | "minimalistic-assert": "^1.0.0", 1395 | "minimalistic-crypto-utils": "^1.0.1" 1396 | } 1397 | }, 1398 | "node_modules/http-errors": { 1399 | "version": "2.0.0", 1400 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 1401 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 1402 | "dependencies": { 1403 | "depd": "2.0.0", 1404 | "inherits": "2.0.4", 1405 | "setprototypeof": "1.2.0", 1406 | "statuses": "2.0.1", 1407 | "toidentifier": "1.0.1" 1408 | }, 1409 | "engines": { 1410 | "node": ">= 0.8" 1411 | } 1412 | }, 1413 | "node_modules/hyper-async": { 1414 | "version": "1.1.2", 1415 | "resolved": "https://registry.npmjs.org/hyper-async/-/hyper-async-1.1.2.tgz", 1416 | "integrity": "sha512-cnpOgKa+5FZOaccTtjduac1FrZuSc38/ftCp3vYJdUMt+7c+uvGDKLDK4MTNK8D3aFjIeveVrPcSgUPvzZLopg==" 1417 | }, 1418 | "node_modules/iconv-lite": { 1419 | "version": "0.4.24", 1420 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", 1421 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", 1422 | "dependencies": { 1423 | "safer-buffer": ">= 2.1.2 < 3" 1424 | }, 1425 | "engines": { 1426 | "node": ">=0.10.0" 1427 | } 1428 | }, 1429 | "node_modules/ieee754": { 1430 | "version": "1.2.1", 1431 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", 1432 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", 1433 | "funding": [ 1434 | { 1435 | "type": "github", 1436 | "url": "https://github.com/sponsors/feross" 1437 | }, 1438 | { 1439 | "type": "patreon", 1440 | "url": "https://www.patreon.com/feross" 1441 | }, 1442 | { 1443 | "type": "consulting", 1444 | "url": "https://feross.org/support" 1445 | } 1446 | ] 1447 | }, 1448 | "node_modules/inherits": { 1449 | "version": "2.0.4", 1450 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1451 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" 1452 | }, 1453 | "node_modules/ipaddr.js": { 1454 | "version": "1.9.1", 1455 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1456 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 1457 | "engines": { 1458 | "node": ">= 0.10" 1459 | } 1460 | }, 1461 | "node_modules/js-sha3": { 1462 | "version": "0.8.0", 1463 | "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", 1464 | "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" 1465 | }, 1466 | "node_modules/lru-cache": { 1467 | "version": "6.0.0", 1468 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1469 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1470 | "dev": true, 1471 | "dependencies": { 1472 | "yallist": "^4.0.0" 1473 | }, 1474 | "engines": { 1475 | "node": ">=10" 1476 | } 1477 | }, 1478 | "node_modules/media-typer": { 1479 | "version": "0.3.0", 1480 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 1481 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", 1482 | "engines": { 1483 | "node": ">= 0.6" 1484 | } 1485 | }, 1486 | "node_modules/merge-descriptors": { 1487 | "version": "1.0.1", 1488 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", 1489 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==" 1490 | }, 1491 | "node_modules/methods": { 1492 | "version": "1.1.2", 1493 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", 1494 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", 1495 | "engines": { 1496 | "node": ">= 0.6" 1497 | } 1498 | }, 1499 | "node_modules/mime": { 1500 | "version": "1.6.0", 1501 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", 1502 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", 1503 | "bin": { 1504 | "mime": "cli.js" 1505 | }, 1506 | "engines": { 1507 | "node": ">=4" 1508 | } 1509 | }, 1510 | "node_modules/mime-db": { 1511 | "version": "1.52.0", 1512 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1513 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1514 | "engines": { 1515 | "node": ">= 0.6" 1516 | } 1517 | }, 1518 | "node_modules/mime-types": { 1519 | "version": "2.1.35", 1520 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1521 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1522 | "dependencies": { 1523 | "mime-db": "1.52.0" 1524 | }, 1525 | "engines": { 1526 | "node": ">= 0.6" 1527 | } 1528 | }, 1529 | "node_modules/minimalistic-assert": { 1530 | "version": "1.0.1", 1531 | "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", 1532 | "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" 1533 | }, 1534 | "node_modules/minimalistic-crypto-utils": { 1535 | "version": "1.0.1", 1536 | "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", 1537 | "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" 1538 | }, 1539 | "node_modules/mnemonist": { 1540 | "version": "0.39.8", 1541 | "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.8.tgz", 1542 | "integrity": "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ==", 1543 | "dependencies": { 1544 | "obliterator": "^2.0.1" 1545 | } 1546 | }, 1547 | "node_modules/ms": { 1548 | "version": "2.0.0", 1549 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 1550 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" 1551 | }, 1552 | "node_modules/negotiator": { 1553 | "version": "0.6.3", 1554 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 1555 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 1556 | "engines": { 1557 | "node": ">= 0.6" 1558 | } 1559 | }, 1560 | "node_modules/object-assign": { 1561 | "version": "4.1.1", 1562 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1563 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1564 | "engines": { 1565 | "node": ">=0.10.0" 1566 | } 1567 | }, 1568 | "node_modules/object-inspect": { 1569 | "version": "1.13.1", 1570 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", 1571 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", 1572 | "funding": { 1573 | "url": "https://github.com/sponsors/ljharb" 1574 | } 1575 | }, 1576 | "node_modules/obliterator": { 1577 | "version": "2.0.4", 1578 | "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", 1579 | "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" 1580 | }, 1581 | "node_modules/on-finished": { 1582 | "version": "2.4.1", 1583 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1584 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1585 | "dependencies": { 1586 | "ee-first": "1.1.1" 1587 | }, 1588 | "engines": { 1589 | "node": ">= 0.8" 1590 | } 1591 | }, 1592 | "node_modules/parseurl": { 1593 | "version": "1.3.3", 1594 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1595 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1596 | "engines": { 1597 | "node": ">= 0.8" 1598 | } 1599 | }, 1600 | "node_modules/path-to-regexp": { 1601 | "version": "0.1.7", 1602 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", 1603 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==" 1604 | }, 1605 | "node_modules/prettier": { 1606 | "version": "3.2.5", 1607 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", 1608 | "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", 1609 | "dev": true, 1610 | "bin": { 1611 | "prettier": "bin/prettier.cjs" 1612 | }, 1613 | "engines": { 1614 | "node": ">=14" 1615 | }, 1616 | "funding": { 1617 | "url": "https://github.com/prettier/prettier?sponsor=1" 1618 | } 1619 | }, 1620 | "node_modules/prettier-plugin-solidity": { 1621 | "version": "1.3.1", 1622 | "resolved": "https://registry.npmjs.org/prettier-plugin-solidity/-/prettier-plugin-solidity-1.3.1.tgz", 1623 | "integrity": "sha512-MN4OP5I2gHAzHZG1wcuJl0FsLS3c4Cc5494bbg+6oQWBPuEamjwDvmGfFMZ6NFzsh3Efd9UUxeT7ImgjNH4ozA==", 1624 | "dev": true, 1625 | "dependencies": { 1626 | "@solidity-parser/parser": "^0.17.0", 1627 | "semver": "^7.5.4", 1628 | "solidity-comments-extractor": "^0.0.8" 1629 | }, 1630 | "engines": { 1631 | "node": ">=16" 1632 | }, 1633 | "peerDependencies": { 1634 | "prettier": ">=2.3.0" 1635 | } 1636 | }, 1637 | "node_modules/proxy-addr": { 1638 | "version": "2.0.7", 1639 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 1640 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 1641 | "dependencies": { 1642 | "forwarded": "0.2.0", 1643 | "ipaddr.js": "1.9.1" 1644 | }, 1645 | "engines": { 1646 | "node": ">= 0.10" 1647 | } 1648 | }, 1649 | "node_modules/proxy-from-env": { 1650 | "version": "1.1.0", 1651 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 1652 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" 1653 | }, 1654 | "node_modules/qs": { 1655 | "version": "6.11.0", 1656 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", 1657 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", 1658 | "dependencies": { 1659 | "side-channel": "^1.0.4" 1660 | }, 1661 | "engines": { 1662 | "node": ">=0.6" 1663 | }, 1664 | "funding": { 1665 | "url": "https://github.com/sponsors/ljharb" 1666 | } 1667 | }, 1668 | "node_modules/ramda": { 1669 | "version": "0.29.1", 1670 | "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.29.1.tgz", 1671 | "integrity": "sha512-OfxIeWzd4xdUNxlWhgFazxsA/nl3mS4/jGZI5n00uWOoSSFRhC1b6gl6xvmzUamgmqELraWp0J/qqVlXYPDPyA==", 1672 | "funding": { 1673 | "type": "opencollective", 1674 | "url": "https://opencollective.com/ramda" 1675 | } 1676 | }, 1677 | "node_modules/range-parser": { 1678 | "version": "1.2.1", 1679 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 1680 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 1681 | "engines": { 1682 | "node": ">= 0.6" 1683 | } 1684 | }, 1685 | "node_modules/raw-body": { 1686 | "version": "2.5.2", 1687 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", 1688 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", 1689 | "dependencies": { 1690 | "bytes": "3.1.2", 1691 | "http-errors": "2.0.0", 1692 | "iconv-lite": "0.4.24", 1693 | "unpipe": "1.0.0" 1694 | }, 1695 | "engines": { 1696 | "node": ">= 0.8" 1697 | } 1698 | }, 1699 | "node_modules/safe-buffer": { 1700 | "version": "5.2.1", 1701 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 1702 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 1703 | "funding": [ 1704 | { 1705 | "type": "github", 1706 | "url": "https://github.com/sponsors/feross" 1707 | }, 1708 | { 1709 | "type": "patreon", 1710 | "url": "https://www.patreon.com/feross" 1711 | }, 1712 | { 1713 | "type": "consulting", 1714 | "url": "https://feross.org/support" 1715 | } 1716 | ] 1717 | }, 1718 | "node_modules/safer-buffer": { 1719 | "version": "2.1.2", 1720 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 1721 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" 1722 | }, 1723 | "node_modules/scrypt-js": { 1724 | "version": "3.0.1", 1725 | "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", 1726 | "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" 1727 | }, 1728 | "node_modules/semver": { 1729 | "version": "7.6.0", 1730 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", 1731 | "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", 1732 | "dev": true, 1733 | "dependencies": { 1734 | "lru-cache": "^6.0.0" 1735 | }, 1736 | "bin": { 1737 | "semver": "bin/semver.js" 1738 | }, 1739 | "engines": { 1740 | "node": ">=10" 1741 | } 1742 | }, 1743 | "node_modules/send": { 1744 | "version": "0.18.0", 1745 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", 1746 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", 1747 | "dependencies": { 1748 | "debug": "2.6.9", 1749 | "depd": "2.0.0", 1750 | "destroy": "1.2.0", 1751 | "encodeurl": "~1.0.2", 1752 | "escape-html": "~1.0.3", 1753 | "etag": "~1.8.1", 1754 | "fresh": "0.5.2", 1755 | "http-errors": "2.0.0", 1756 | "mime": "1.6.0", 1757 | "ms": "2.1.3", 1758 | "on-finished": "2.4.1", 1759 | "range-parser": "~1.2.1", 1760 | "statuses": "2.0.1" 1761 | }, 1762 | "engines": { 1763 | "node": ">= 0.8.0" 1764 | } 1765 | }, 1766 | "node_modules/send/node_modules/ms": { 1767 | "version": "2.1.3", 1768 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1769 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 1770 | }, 1771 | "node_modules/serve-static": { 1772 | "version": "1.15.0", 1773 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", 1774 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", 1775 | "dependencies": { 1776 | "encodeurl": "~1.0.2", 1777 | "escape-html": "~1.0.3", 1778 | "parseurl": "~1.3.3", 1779 | "send": "0.18.0" 1780 | }, 1781 | "engines": { 1782 | "node": ">= 0.8.0" 1783 | } 1784 | }, 1785 | "node_modules/set-function-length": { 1786 | "version": "1.2.1", 1787 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz", 1788 | "integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==", 1789 | "dependencies": { 1790 | "define-data-property": "^1.1.2", 1791 | "es-errors": "^1.3.0", 1792 | "function-bind": "^1.1.2", 1793 | "get-intrinsic": "^1.2.3", 1794 | "gopd": "^1.0.1", 1795 | "has-property-descriptors": "^1.0.1" 1796 | }, 1797 | "engines": { 1798 | "node": ">= 0.4" 1799 | } 1800 | }, 1801 | "node_modules/setprototypeof": { 1802 | "version": "1.2.0", 1803 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 1804 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" 1805 | }, 1806 | "node_modules/side-channel": { 1807 | "version": "1.0.6", 1808 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", 1809 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", 1810 | "dependencies": { 1811 | "call-bind": "^1.0.7", 1812 | "es-errors": "^1.3.0", 1813 | "get-intrinsic": "^1.2.4", 1814 | "object-inspect": "^1.13.1" 1815 | }, 1816 | "engines": { 1817 | "node": ">= 0.4" 1818 | }, 1819 | "funding": { 1820 | "url": "https://github.com/sponsors/ljharb" 1821 | } 1822 | }, 1823 | "node_modules/solidity-comments-extractor": { 1824 | "version": "0.0.8", 1825 | "resolved": "https://registry.npmjs.org/solidity-comments-extractor/-/solidity-comments-extractor-0.0.8.tgz", 1826 | "integrity": "sha512-htM7Vn6LhHreR+EglVMd2s+sZhcXAirB1Zlyrv5zBuTxieCvjfnRpd7iZk75m/u6NOlEyQ94C6TWbBn2cY7w8g==", 1827 | "dev": true 1828 | }, 1829 | "node_modules/statuses": { 1830 | "version": "2.0.1", 1831 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 1832 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 1833 | "engines": { 1834 | "node": ">= 0.8" 1835 | } 1836 | }, 1837 | "node_modules/toidentifier": { 1838 | "version": "1.0.1", 1839 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 1840 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 1841 | "engines": { 1842 | "node": ">=0.6" 1843 | } 1844 | }, 1845 | "node_modules/type-is": { 1846 | "version": "1.6.18", 1847 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", 1848 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", 1849 | "dependencies": { 1850 | "media-typer": "0.3.0", 1851 | "mime-types": "~2.1.24" 1852 | }, 1853 | "engines": { 1854 | "node": ">= 0.6" 1855 | } 1856 | }, 1857 | "node_modules/undici": { 1858 | "version": "5.28.3", 1859 | "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.3.tgz", 1860 | "integrity": "sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA==", 1861 | "dependencies": { 1862 | "@fastify/busboy": "^2.0.0" 1863 | }, 1864 | "engines": { 1865 | "node": ">=14.0" 1866 | } 1867 | }, 1868 | "node_modules/unpipe": { 1869 | "version": "1.0.0", 1870 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 1871 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 1872 | "engines": { 1873 | "node": ">= 0.8" 1874 | } 1875 | }, 1876 | "node_modules/utils-merge": { 1877 | "version": "1.0.1", 1878 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1879 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", 1880 | "engines": { 1881 | "node": ">= 0.4.0" 1882 | } 1883 | }, 1884 | "node_modules/vary": { 1885 | "version": "1.1.2", 1886 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1887 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 1888 | "engines": { 1889 | "node": ">= 0.8" 1890 | } 1891 | }, 1892 | "node_modules/warp-arbundles": { 1893 | "version": "1.0.4", 1894 | "resolved": "https://registry.npmjs.org/warp-arbundles/-/warp-arbundles-1.0.4.tgz", 1895 | "integrity": "sha512-KeRac/EJ7VOK+v5+PSMh2SrzpCKOAFnJICLlqZWt6qPkDCzVwcrNE5wFxOlEk5U170ewMDAB3e86UHUblevXpw==", 1896 | "dependencies": { 1897 | "arweave": "^1.13.7", 1898 | "base64url": "^3.0.1", 1899 | "buffer": "^6.0.3", 1900 | "warp-isomorphic": "^1.0.7" 1901 | } 1902 | }, 1903 | "node_modules/warp-isomorphic": { 1904 | "version": "1.0.7", 1905 | "resolved": "https://registry.npmjs.org/warp-isomorphic/-/warp-isomorphic-1.0.7.tgz", 1906 | "integrity": "sha512-fXHbUXwdYqPm9fRPz8mjv5ndPco09aMQuTe4kXfymzOq8V6F3DLsg9cIafxvjms9/mc6eijzkLBJ63yjEENEjA==", 1907 | "dependencies": { 1908 | "buffer": "^6.0.3", 1909 | "undici": "^5.19.1" 1910 | }, 1911 | "engines": { 1912 | "node": ">=16.8.0" 1913 | } 1914 | }, 1915 | "node_modules/ws": { 1916 | "version": "7.4.6", 1917 | "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", 1918 | "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", 1919 | "engines": { 1920 | "node": ">=8.3.0" 1921 | }, 1922 | "peerDependencies": { 1923 | "bufferutil": "^4.0.1", 1924 | "utf-8-validate": "^5.0.2" 1925 | }, 1926 | "peerDependenciesMeta": { 1927 | "bufferutil": { 1928 | "optional": true 1929 | }, 1930 | "utf-8-validate": { 1931 | "optional": true 1932 | } 1933 | } 1934 | }, 1935 | "node_modules/yallist": { 1936 | "version": "4.0.0", 1937 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 1938 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 1939 | "dev": true 1940 | }, 1941 | "node_modules/zod": { 1942 | "version": "3.22.4", 1943 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", 1944 | "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", 1945 | "funding": { 1946 | "url": "https://github.com/sponsors/colinhacks" 1947 | } 1948 | } 1949 | } 1950 | } 1951 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mem-bridge", 3 | "version": "0.1.2", 4 | "description": "", 5 | "main": "index.js", 6 | "type": "module", 7 | "scripts": { 8 | "start": "node ./src/index.js", 9 | "prettify": "npx prettier --write --plugin=prettier-plugin-solidity contracts/**/*.sol" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "@permaweb/aoconnect": "^0.0.48", 15 | "axios": "^1.6.7", 16 | "body-parser": "^1.20.2", 17 | "cors": "^2.8.5", 18 | "dotenv": "^16.4.5", 19 | "ethers": "^5.7.0", 20 | "express": "^4.18.3" 21 | }, 22 | "devDependencies": { 23 | "prettier": "^3.2.5", 24 | "prettier-plugin-solidity": "^1.3.1" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import bodyParser from "body-parser"; 3 | import cors from "cors"; 4 | import dotenv from "dotenv"; 5 | import axios from "axios"; 6 | 7 | import { validateUnlock, callEvmValidateUnlock } from "./utils/memTx.js"; 8 | import { validateLock } from "./utils/ethTx.js"; 9 | import { executeMemAoLock } from "./utils/aoMint.js"; 10 | 11 | dotenv.config(); 12 | const port = process.env.PORT || 3000; 13 | const app = express(); 14 | 15 | app.use( 16 | cors({ 17 | origin: "*", 18 | }), 19 | ); 20 | 21 | app.use(bodyParser.json({ limit: "50mb" })); 22 | 23 | app.use((err, req, res, next) => { 24 | res.status(500).send({ error: "invalid JSON input" }); 25 | return; 26 | }); 27 | 28 | // VU: Validate Unlock in the solidity `validateUnlock(string)` function 29 | app.get("/vu/:mid/:caller", async (req, res) => { 30 | try { 31 | const { mid, caller } = req.params; 32 | const amount = await validateUnlock(mid, caller); 33 | res.json(amount); // unlock.amount 34 | return; 35 | } catch (error) { 36 | console.log(error); 37 | res.json({ amount: 0 }); 38 | return; 39 | } 40 | }); 41 | 42 | app.get("/vl/:txid/:caller/:bridgeAddr", async (req, res) => { 43 | try { 44 | const { txid, caller, bridgeAddr } = req.params; 45 | const result = await validateLock(txid, caller, bridgeAddr); 46 | res.json(result); // unlock.amount 47 | return; 48 | } catch (error) { 49 | console.log(error); 50 | res.json({ caller: null }); 51 | return; 52 | } 53 | }); 54 | 55 | // AL: execute the AO LOCK on MEM to AO 56 | app.get("/al/:mid", async (req, res) => { 57 | try { 58 | const { mid } = req.params; 59 | const result = await executeMemAoLock(mid); 60 | res.json(result); 61 | return; 62 | } catch (error) { 63 | console.log(error); 64 | res.json({ messageId: null }); 65 | return; 66 | } 67 | }); 68 | 69 | // cronjob EOA call the validateUnlock() in bridge.sol 70 | app.get("/server/:mid/:caller", async (req, res) => { 71 | try { 72 | const { mid, caller } = req.params; 73 | const result = await callEvmValidateUnlock(mid, caller); 74 | res.json(result); 75 | return; 76 | } catch (error) { 77 | console.log(error); 78 | res.json({ requestId: null }); 79 | return; 80 | } 81 | }); 82 | 83 | app.listen(port); 84 | -------------------------------------------------------------------------------- /src/utils/aoMint.js: -------------------------------------------------------------------------------- 1 | import { dryrun, createDataItemSigner, message } from "@permaweb/aoconnect"; 2 | import { AO_PROCESS_ID, MEM_ORACLE_ID } from "./constants.js"; 3 | import dotenv from "dotenv"; 4 | import axios from "axios"; 5 | import assert from "node:assert"; 6 | 7 | dotenv.config(); 8 | 9 | const wallet = JSON.parse(process.env.JWK); 10 | 11 | export async function executeMemAoLock(id) { 12 | try { 13 | const memIdsInAo = await getMemIds(); 14 | assert.equal(!memIdsInAo[id], true); 15 | const locks = ( 16 | await axios.get(`https://api.mem.tech/api/state/${MEM_ORACLE_ID}`) 17 | )?.data?.aoLocks; 18 | 19 | const lockIndex = locks.findIndex((lock) => lock.id === id); 20 | assert.equal(lockIndex >= 0, true); 21 | const lock = locks[lockIndex]; 22 | 23 | return await mintFor(lock.ao_address, id, lock.amount); 24 | } catch (error) { 25 | console.log(error); 26 | return false; 27 | } 28 | } 29 | 30 | async function mintFor(address, memId, qty) { 31 | try { 32 | const messageId = await message({ 33 | process: AO_PROCESS_ID, 34 | signer: createDataItemSigner(wallet), 35 | data: "", 36 | tags: [ 37 | { name: "Action", value: "Mint" }, 38 | { 39 | name: "Address", 40 | value: address, 41 | }, 42 | { name: "Quantity", value: qty }, 43 | { name: "MemId", value: memId }, 44 | ], 45 | }); 46 | 47 | console.log(messageId); 48 | return { messageId }; 49 | } catch (error) { 50 | console.log(error); 51 | return { messageId: false }; 52 | } 53 | } 54 | 55 | async function getMemIds() { 56 | try { 57 | const tx = await dryrun({ 58 | process: AO_PROCESS_ID, 59 | tags: [{ name: "Action", value: "GetMemIds" }], 60 | }); 61 | 62 | return JSON.parse(tx.Messages[0].Data); 63 | } catch (error) { 64 | console.log(error); 65 | return {}; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/utils/constants.js: -------------------------------------------------------------------------------- 1 | export const BRIDGES_CONTRACTS = { 2 | "0x842b64bBA4D3bc5Cb29A7Bf73813a01CF684AF4a": { 3 | name: "usdc_token", 4 | decimals: 1e6, 5 | }, 6 | }; 7 | 8 | export const USDC_TOKEN_BRIDGE = 9 | "0x842b64bBA4D3bc5Cb29A7Bf73813a01CF684AF4a"; 10 | export const MEM_ORACLE_ID = `0RrHAKlG6Gn-M_qk29ZWdRZzO0PAQyZeMFC-y3PF8m8`; 11 | export const AO_PROCESS_ID = `oDMJXlSOhJ6UjH5i7Dl-UOr_dhS1rQCX4r9ws0jvFps`; // xtvzEpBJfkrKz8FRxwFUkP3q5x5OOWDn3LE6bkf0MT0 12 | 13 | export const RPC_URL = `https://rpc.sepolia.org/`; 14 | 15 | export const BRIDGE_ABI = [ 16 | "function validateUnlock(string calldata _memid, address _caller) public returns (bytes32 requestId)", 17 | ]; 18 | -------------------------------------------------------------------------------- /src/utils/ethTx.js: -------------------------------------------------------------------------------- 1 | import { BRIDGES_CONTRACTS, RPC_URL } from "./constants.js"; 2 | 3 | import { ethers } from "ethers"; 4 | import assert from "node:assert"; 5 | 6 | export async function validateLock(txid, expectedCaller, tokenContractAddr) { 7 | try { 8 | const normalized = ethers.utils.getAddress; 9 | assert(tokenContractAddr in BRIDGES_CONTRACTS, true); 10 | // Set up provider for the Sepolia network 11 | const provider = new ethers.providers.JsonRpcProvider(RPC_URL); 12 | const currentBlockNumber = await provider.getBlockNumber(); 13 | 14 | const receipt = await provider.getTransactionReceipt(txid); 15 | const abi = ["event Lock (address target, uint256 amount)"]; 16 | const iface = new ethers.utils.Interface(abi); 17 | 18 | const log = iface.parseLog(receipt.logs[1]); 19 | 20 | assert.equal(receipt.to, tokenContractAddr); 21 | assert.equal(normalized(receipt.from), normalized(expectedCaller)); 22 | assert.equal(receipt.transactionHash, txid); 23 | assert.equal(Boolean(receipt.blockNumber), true); 24 | assert.equal(receipt.blockNumber + 3 < currentBlockNumber, true); 25 | const { args, name, signature } = log; 26 | assert.equal(signature, "Lock(address,uint256)"); 27 | 28 | const target = args[0]; 29 | const amount = BigInt(args[1].toString()).toString(); 30 | 31 | console.log({ 32 | caller: target, 33 | amount, 34 | txid, 35 | tokenContractAddr, 36 | }); 37 | return { 38 | caller: target, 39 | amount, 40 | }; 41 | } catch (error) { 42 | console.error("Error:", error); 43 | return { 44 | sender: false, 45 | }; 46 | } 47 | } 48 | 49 | export async function getRequestIdFromTxid(txid) { 50 | try { 51 | const provider = new ethers.providers.JsonRpcProvider(RPC_URL); 52 | const receipt = await provider.getTransactionReceipt(txid); 53 | return receipt.logs[0].topics[1]; 54 | } catch (error) { 55 | console.error("Error:", error); 56 | return null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/utils/memTx.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { ethers } from "ethers"; 3 | import { 4 | MEM_ORACLE_ID, 5 | USDC_TOKEN_BRIDGE, 6 | BRIDGE_ABI, 7 | RPC_URL, 8 | } from "./constants.js"; 9 | import { getRequestIdFromTxid } from "./ethTx.js"; 10 | import dotenv from "dotenv"; 11 | 12 | dotenv.config(); 13 | 14 | export async function validateUnlock(memid, caller) { 15 | try { 16 | const normalized = ethers.utils.getAddress; 17 | const contractState = ( 18 | await axios.get(`https://api.mem.tech/api/state/${MEM_ORACLE_ID}`) 19 | )?.data?.unlocks; 20 | const unlock = contractState.find( 21 | (req) => 22 | req.mid === memid && normalized(req.address) == normalized(caller), 23 | ); 24 | return { amount: Number(unlock.amount) }; 25 | } catch (error) { 26 | console.log(error); 27 | return { amount: 0 }; 28 | } 29 | } 30 | 31 | async function getMemIssuedUnlocks(memid, caller) { 32 | try { 33 | const normalized = ethers.utils.getAddress; 34 | const contractState = ( 35 | await axios.get(`https://api.mem.tech/api/state/${MEM_ORACLE_ID}`) 36 | )?.data?.unlocks; 37 | const unlock = contractState.find( 38 | (req) => 39 | req.mid === memid && normalized(req.address) == normalized(caller), 40 | ); 41 | return unlock; 42 | } catch (error) { 43 | console.log(error); 44 | return {}; 45 | } 46 | } 47 | 48 | export async function callEvmValidateUnlock(memid, caller) { 49 | try { 50 | const provider = new ethers.providers.JsonRpcProvider(RPC_URL); 51 | const signer = new ethers.Wallet(process.env.CRONJOB_PK, provider); 52 | const BridgeContract = new ethers.Contract( 53 | USDC_TOKEN_BRIDGE, 54 | BRIDGE_ABI, 55 | signer, 56 | ); 57 | 58 | const tx = await BridgeContract.validateUnlock(memid, caller); 59 | await tx.wait(); 60 | const requestId = await getRequestIdFromTxid(tx.hash); 61 | return { requestId }; 62 | } catch (error) { 63 | console.log(error); 64 | return { requestId: null }; 65 | } 66 | } 67 | --------------------------------------------------------------------------------