PinkLock02
0xbe0b139abc90723af76a89d3051f60ba1b64c8d9
Verification
Verified
v0.8.4+commit.c7e474f2
Type
Contract
20,622 bytes
ABI entries
35
21 read · 8 write
License
none
Contract information
- Address
- 0xbe0b139abc90723af76a89d3051f60ba1b64c8d9
- Chain
- Robinhood Chain (4663)
- Compiler
- v0.8.4+commit.c7e474f2
- Optimization
- Enabled
- Creator
- 0x802651539e…c8f0DD5605
- Creation tx
- 0x2e2cbfe712…cdad224e76
Token
Not a token
This contract does not expose ERC-20 metadata.
Read contract (21)
allLpTokenLockedCount() → uint256
allNormalTokenLockedCount() → uint256
cumulativeLockInfo(address) → address, address, uint256
getCumulativeLpTokenLockInfo(uint256, uint256) → tuple[]
getCumulativeLpTokenLockInfoAt(uint256) → tuple
getCumulativeNormalTokenLockInfo(uint256, uint256) → tuple[]
getCumulativeNormalTokenLockInfoAt(uint256) → tuple
getLockAt(uint256) → tuple
getLockById(uint256) → tuple
getLocksForToken(address, uint256, uint256) → tuple[]
getTotalLockCount() → uint256
lpLockCountForUser(address) → uint256
lpLockForUserAtIndex(address, uint256) → tuple
lpLocksForUser(address) → tuple[]
normalLockCountForUser(address) → uint256
normalLockForUserAtIndex(address, uint256) → tuple
normalLocksForUser(address) → tuple[]
totalLockCountForToken(address) → uint256
totalLockCountForUser(address) → uint256
totalTokenLockedCount() → uint256
withdrawableTokens(uint256) → uint256
Events (6)
LockAddedLockDescriptionChangedLockOwnerChangedLockRemovedLockUpdatedLockVested
ABI
[
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "unlockDate",
"type": "uint256"
}
],
"name": "LockAdded",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
}
],
"name": "LockDescriptionChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": false,
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "LockOwnerChanged",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "unlockedAt",
"type": "uint256"
}
],
"name": "LockRemoved",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newAmount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "newUnlockDate",
"type": "uint256"
}
],
"name": "LockUpdated",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"indexed": false,
"internalType": "address",
"name": "token",
"type": "address"
},
{
"indexed": false,
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "remaining",
"type": "uint256"
},
{
"indexed": false,
"internalType": "uint256",
"name": "timestamp",
"type": "uint256"
}
],
"name": "LockVested",
"type": "event"
},
{
"inputs": [],
"name": "allLpTokenLockedCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "allNormalTokenLockedCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "",
"type": "address"
}
],
"name": "cumulativeLockInfo",
"outputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "factory",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "newAmount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "newUnlockDate",
"type": "uint256"
}
],
"name": "editLock",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"name": "editLockDescription",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "start",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "end",
"type": "uint256"
}
],
"name": "getCumulativeLpTokenLockInfo",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "factory",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct PinkLock02.CumulativeLockInfo[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "getCumulativeLpTokenLockInfoAt",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "factory",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct PinkLock02.CumulativeLockInfo",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "start",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "end",
"type": "uint256"
}
],
"name": "getCumulativeNormalTokenLockInfo",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "factory",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct PinkLock02.CumulativeLockInfo[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "getCumulativeNormalTokenLockInfoAt",
"outputs": [
{
"components": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "factory",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
}
],
"internalType": "struct PinkLock02.CumulativeLockInfo",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "getLockAt",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lockDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockedAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"internalType": "struct PinkLock02.Lock",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
}
],
"name": "getLockById",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lockDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockedAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"internalType": "struct PinkLock02.Lock",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "uint256",
"name": "start",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "end",
"type": "uint256"
}
],
"name": "getLocksForToken",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lockDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockedAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"internalType": "struct PinkLock02.Lock[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "getTotalLockCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "bool",
"name": "isLpToken",
"type": "bool"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockDate",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"name": "lock",
"outputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
}
],
"name": "lpLockCountForUser",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
},
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "lpLockForUserAtIndex",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lockDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockedAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"internalType": "struct PinkLock02.Lock",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
}
],
"name": "lpLocksForUser",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lockDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockedAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"internalType": "struct PinkLock02.Lock[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address[]",
"name": "owners",
"type": "address[]"
},
{
"internalType": "uint256[]",
"name": "amounts",
"type": "uint256[]"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "bool",
"name": "isLpToken",
"type": "bool"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"name": "multipleVestingLock",
"outputs": [
{
"internalType": "uint256[]",
"name": "",
"type": "uint256[]"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
}
],
"name": "normalLockCountForUser",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
},
{
"internalType": "uint256",
"name": "index",
"type": "uint256"
}
],
"name": "normalLockForUserAtIndex",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lockDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockedAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"internalType": "struct PinkLock02.Lock",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
}
],
"name": "normalLocksForUser",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lockDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "unlockedAmount",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"internalType": "struct PinkLock02.Lock[]",
"name": "",
"type": "tuple[]"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
}
],
"name": "renounceLockOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "token",
"type": "address"
}
],
"name": "totalLockCountForToken",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
}
],
"name": "totalLockCountForUser",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "totalTokenLockedCount",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
},
{
"internalType": "address",
"name": "newOwner",
"type": "address"
}
],
"name": "transferLockOwnership",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
}
],
"name": "unlock",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "owner",
"type": "address"
},
{
"internalType": "address",
"name": "token",
"type": "address"
},
{
"internalType": "bool",
"name": "isLpToken",
"type": "bool"
},
{
"internalType": "uint256",
"name": "amount",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeDate",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "tgeBps",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycle",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "cycleBps",
"type": "uint256"
},
{
"internalType": "string",
"name": "description",
"type": "string"
}
],
"name": "vestingLock",
"outputs": [
{
"internalType": "uint256",
"name": "id",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "lockId",
"type": "uint256"
}
],
"name": "withdrawableTokens",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
}
]Source code
// SPDX-License-Identifier: MIT
pragma solidity =0.8.4;
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "./IPinkLock.sol";
import "./IUniswapV2Router02.sol";
import "./IUniswapV2Pair.sol";
import "./IUniswapV2Factory.sol";
import "./FullMath.sol";
contract PinkLock02 is IPinkLock {
using Address for address payable;
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.UintSet;
using SafeERC20 for IERC20;
struct Lock {
uint256 id;
address token;
address owner;
uint256 amount;
uint256 lockDate;
uint256 tgeDate; // TGE date for vesting locks, unlock date for normal locks
uint256 tgeBps; // In bips. Is 0 for normal locks
uint256 cycle; // Is 0 for normal locks
uint256 cycleBps; // In bips. Is 0 for normal locks
uint256 unlockedAmount;
string description;
}
struct CumulativeLockInfo {
address token;
address factory;
uint256 amount;
}
// ID padding from PinkLock v1, as there is a lack of a pausing mechanism
// as of now the lastest id from v1 is about 22K, so this is probably a safe padding value.
uint256 private constant ID_PADDING = 1_000_000;
Lock[] private _locks;
mapping(address => EnumerableSet.UintSet) private _userLpLockIds;
mapping(address => EnumerableSet.UintSet) private _userNormalLockIds;
EnumerableSet.AddressSet private _lpLockedTokens;
EnumerableSet.AddressSet private _normalLockedTokens;
mapping(address => CumulativeLockInfo) public cumulativeLockInfo;
mapping(address => EnumerableSet.UintSet) private _tokenToLockIds;
event LockAdded(
uint256 indexed id,
address token,
address owner,
uint256 amount,
uint256 unlockDate
);
event LockUpdated(
uint256 indexed id,
address token,
address owner,
uint256 newAmount,
uint256 newUnlockDate
);
event LockRemoved(
uint256 indexed id,
address token,
address owner,
uint256 amount,
uint256 unlockedAt
);
event LockVested(
uint256 indexed id,
address token,
address owner,
uint256 amount,
uint256 remaining,
uint256 timestamp
);
event LockDescriptionChanged(uint256 lockId);
event LockOwnerChanged(uint256 lockId, address owner, address newOwner);
modifier validLock(uint256 lockId) {
_getActualIndex(lockId);
_;
}
function lock(
address owner,
address token,
bool isLpToken,
uint256 amount,
uint256 unlockDate,
string memory description
) external override returns (uint256 id) {
require(token != address(0), "Invalid token");
require(amount > 0, "Amount should be greater than 0");
require(
unlockDate > block.timestamp,
"Unlock date should be in the future"
);
id = _createLock(
owner,
token,
isLpToken,
amount,
unlockDate,
0,
0,
0,
description
);
_safeTransferFromEnsureExactAmount(
token,
msg.sender,
address(this),
amount
);
emit LockAdded(id, token, owner, amount, unlockDate);
return id;
}
function vestingLock(
address owner,
address token,
bool isLpToken,
uint256 amount,
uint256 tgeDate,
uint256 tgeBps,
uint256 cycle,
uint256 cycleBps,
string memory description
) external override returns (uint256 id) {
require(token != address(0), "Invalid token");
require(amount > 0, "Amount should be greater than 0");
require(tgeDate > block.timestamp, "TGE date should be in the future");
require(cycle > 0, "Invalid cycle");
require(tgeBps > 0 && tgeBps < 10_000, "Invalid bips for TGE");
require(cycleBps > 0 && cycleBps < 10_000, "Invalid bips for cycle");
require(
tgeBps + cycleBps <= 10_000,
"Sum of TGE bps and cycle should be less than 10000"
);
id = _createLock(
owner,
token,
isLpToken,
amount,
tgeDate,
tgeBps,
cycle,
cycleBps,
description
);
_safeTransferFromEnsureExactAmount(
token,
msg.sender,
address(this),
amount
);
emit LockAdded(id, token, owner, amount, tgeDate);
return id;
}
function multipleVestingLock(
address[] calldata owners,
uint256[] calldata amounts,
address token,
bool isLpToken,
uint256 tgeDate,
uint256 tgeBps,
uint256 cycle,
uint256 cycleBps,
string memory description
) external override returns (uint256[] memory) {
require(token != address(0), "Invalid token");
require(owners.length == amounts.length, "Length mismatched");
require(tgeDate > block.timestamp, "TGE date should be in the future");
require(cycle > 0, "Invalid cycle");
require(tgeBps > 0 && tgeBps < 10_000, "Invalid bips for TGE");
require(cycleBps > 0 && cycleBps < 10_000, "Invalid bips for cycle");
require(
tgeBps + cycleBps <= 10_000,
"Sum of TGE bps and cycle should be less than 10000"
);
return
_multipleVestingLock(
owners,
amounts,
token,
isLpToken,
[tgeDate, tgeBps, cycle, cycleBps],
description
);
}
function _multipleVestingLock(
address[] calldata owners,
uint256[] calldata amounts,
address token,
bool isLpToken,
uint256[4] memory vestingSettings, // avoid stack too deep
string memory description
) internal returns (uint256[] memory) {
require(token != address(0), "Invalid token");
uint256 sumAmount = _sumAmount(amounts);
uint256 count = owners.length;
uint256[] memory ids = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
ids[i] = _createLock(
owners[i],
token,
isLpToken,
amounts[i],
vestingSettings[0], // TGE date
vestingSettings[1], // TGE bps
vestingSettings[2], // cycle
vestingSettings[3], // cycle bps
description
);
emit LockAdded(
ids[i],
token,
owners[i],
amounts[i],
vestingSettings[0] // TGE date
);
}
_safeTransferFromEnsureExactAmount(
token,
msg.sender,
address(this),
sumAmount
);
return ids;
}
function _sumAmount(uint256[] calldata amounts)
internal
pure
returns (uint256)
{
uint256 sum = 0;
for (uint256 i = 0; i < amounts.length; i++) {
if (amounts[i] == 0) {
revert("Amount cant be zero");
}
sum += amounts[i];
}
return sum;
}
function _createLock(
address owner,
address token,
bool isLpToken,
uint256 amount,
uint256 tgeDate,
uint256 tgeBps,
uint256 cycle,
uint256 cycleBps,
string memory description
) internal returns (uint256 id) {
if (isLpToken) {
address possibleFactoryAddress = _parseFactoryAddress(token);
id = _lockLpToken(
owner,
token,
possibleFactoryAddress,
amount,
tgeDate,
tgeBps,
cycle,
cycleBps,
description
);
} else {
id = _lockNormalToken(
owner,
token,
amount,
tgeDate,
tgeBps,
cycle,
cycleBps,
description
);
}
return id;
}
function _lockLpToken(
address owner,
address token,
address factory,
uint256 amount,
uint256 tgeDate,
uint256 tgeBps,
uint256 cycle,
uint256 cycleBps,
string memory description
) private returns (uint256 id) {
id = _registerLock(
owner,
token,
amount,
tgeDate,
tgeBps,
cycle,
cycleBps,
description
);
_userLpLockIds[owner].add(id);
_lpLockedTokens.add(token);
CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[token];
if (tokenInfo.token == address(0)) {
tokenInfo.token = token;
tokenInfo.factory = factory;
}
tokenInfo.amount = tokenInfo.amount + amount;
_tokenToLockIds[token].add(id);
}
function _lockNormalToken(
address owner,
address token,
uint256 amount,
uint256 tgeDate,
uint256 tgeBps,
uint256 cycle,
uint256 cycleBps,
string memory description
) private returns (uint256 id) {
id = _registerLock(
owner,
token,
amount,
tgeDate,
tgeBps,
cycle,
cycleBps,
description
);
_userNormalLockIds[owner].add(id);
_normalLockedTokens.add(token);
CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[token];
if (tokenInfo.token == address(0)) {
tokenInfo.token = token;
tokenInfo.factory = address(0);
}
tokenInfo.amount = tokenInfo.amount + amount;
_tokenToLockIds[token].add(id);
}
function _registerLock(
address owner,
address token,
uint256 amount,
uint256 tgeDate,
uint256 tgeBps,
uint256 cycle,
uint256 cycleBps,
string memory description
) private returns (uint256 id) {
id = _locks.length + ID_PADDING;
Lock memory newLock = Lock({
id: id,
token: token,
owner: owner,
amount: amount,
lockDate: block.timestamp,
tgeDate: tgeDate,
tgeBps: tgeBps,
cycle: cycle,
cycleBps: cycleBps,
unlockedAmount: 0,
description: description
});
_locks.push(newLock);
}
function unlock(uint256 lockId) external override validLock(lockId) {
Lock storage userLock = _locks[_getActualIndex(lockId)];
require(
userLock.owner == msg.sender,
"You are not the owner of this lock"
);
if (userLock.tgeBps > 0) {
_vestingUnlock(userLock);
} else {
_normalUnlock(userLock);
}
}
function _normalUnlock(Lock storage userLock) internal {
require(
block.timestamp >= userLock.tgeDate,
"It is not time to unlock"
);
require(userLock.unlockedAmount == 0, "Nothing to unlock");
CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[
userLock.token
];
bool isLpToken = tokenInfo.factory != address(0);
if (isLpToken) {
_userLpLockIds[msg.sender].remove(userLock.id);
} else {
_userNormalLockIds[msg.sender].remove(userLock.id);
}
uint256 unlockAmount = userLock.amount;
if (tokenInfo.amount <= unlockAmount) {
tokenInfo.amount = 0;
} else {
tokenInfo.amount = tokenInfo.amount - unlockAmount;
}
if (tokenInfo.amount == 0) {
if (isLpToken) {
_lpLockedTokens.remove(userLock.token);
} else {
_normalLockedTokens.remove(userLock.token);
}
}
userLock.unlockedAmount = unlockAmount;
_tokenToLockIds[userLock.token].remove(userLock.id);
IERC20(userLock.token).safeTransfer(msg.sender, unlockAmount);
emit LockRemoved(
userLock.id,
userLock.token,
msg.sender,
unlockAmount,
block.timestamp
);
}
function _vestingUnlock(Lock storage userLock) internal {
uint256 withdrawable = _withdrawableTokens(userLock);
uint256 newTotalUnlockAmount = userLock.unlockedAmount + withdrawable;
require(
withdrawable > 0 && newTotalUnlockAmount <= userLock.amount,
"Nothing to unlock"
);
CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[
userLock.token
];
bool isLpToken = tokenInfo.factory != address(0);
if (newTotalUnlockAmount == userLock.amount) {
if (isLpToken) {
_userLpLockIds[msg.sender].remove(userLock.id);
} else {
_userNormalLockIds[msg.sender].remove(userLock.id);
}
_tokenToLockIds[userLock.token].remove(userLock.id);
emit LockRemoved(
userLock.id,
userLock.token,
msg.sender,
newTotalUnlockAmount,
block.timestamp
);
}
if (tokenInfo.amount <= withdrawable) {
tokenInfo.amount = 0;
} else {
tokenInfo.amount = tokenInfo.amount - withdrawable;
}
if (tokenInfo.amount == 0) {
if (isLpToken) {
_lpLockedTokens.remove(userLock.token);
} else {
_normalLockedTokens.remove(userLock.token);
}
}
userLock.unlockedAmount = newTotalUnlockAmount;
IERC20(userLock.token).safeTransfer(userLock.owner, withdrawable);
emit LockVested(
userLock.id,
userLock.token,
msg.sender,
withdrawable,
userLock.amount - userLock.unlockedAmount,
block.timestamp
);
}
function withdrawableTokens(uint256 lockId)
external
view
returns (uint256)
{
Lock memory userLock = getLockById(lockId);
return _withdrawableTokens(userLock);
}
function _withdrawableTokens(Lock memory userLock)
internal
view
returns (uint256)
{
if (userLock.amount == 0) return 0;
if (userLock.unlockedAmount >= userLock.amount) return 0;
if (block.timestamp < userLock.tgeDate) return 0;
if (userLock.cycle == 0) return 0;
uint256 tgeReleaseAmount = FullMath.mulDiv(
userLock.amount,
userLock.tgeBps,
10_000
);
uint256 cycleReleaseAmount = FullMath.mulDiv(
userLock.amount,
userLock.cycleBps,
10_000
);
uint256 currentTotal = 0;
if (block.timestamp >= userLock.tgeDate) {
currentTotal =
(((block.timestamp - userLock.tgeDate) / userLock.cycle) *
cycleReleaseAmount) +
tgeReleaseAmount; // Truncation is expected here
}
uint256 withdrawable = 0;
if (currentTotal > userLock.amount) {
withdrawable = userLock.amount - userLock.unlockedAmount;
} else {
withdrawable = currentTotal - userLock.unlockedAmount;
}
return withdrawable;
}
function editLock(
uint256 lockId,
uint256 newAmount,
uint256 newUnlockDate
) external override validLock(lockId) {
Lock storage userLock = _locks[_getActualIndex(lockId)];
require(
userLock.owner == msg.sender,
"You are not the owner of this lock"
);
require(userLock.unlockedAmount == 0, "Lock was unlocked");
if (newUnlockDate > 0) {
require(
newUnlockDate >= userLock.tgeDate &&
newUnlockDate > block.timestamp,
"New unlock time should not be before old unlock time or current time"
);
userLock.tgeDate = newUnlockDate;
}
if (newAmount > 0) {
require(
newAmount >= userLock.amount,
"New amount should not be less than current amount"
);
uint256 diff = newAmount - userLock.amount;
if (diff > 0) {
userLock.amount = newAmount;
CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[
userLock.token
];
tokenInfo.amount = tokenInfo.amount + diff;
_safeTransferFromEnsureExactAmount(
userLock.token,
msg.sender,
address(this),
diff
);
}
}
emit LockUpdated(
userLock.id,
userLock.token,
userLock.owner,
userLock.amount,
userLock.tgeDate
);
}
function editLockDescription(uint256 lockId, string memory description)
external
validLock(lockId)
{
Lock storage userLock = _locks[_getActualIndex(lockId)];
require(
userLock.owner == msg.sender,
"You are not the owner of this lock"
);
userLock.description = description;
emit LockDescriptionChanged(lockId);
}
function transferLockOwnership(uint256 lockId, address newOwner)
public
validLock(lockId)
{
Lock storage userLock = _locks[_getActualIndex(lockId)];
address currentOwner = userLock.owner;
require(
currentOwner == msg.sender,
"You are not the owner of this lock"
);
userLock.owner = newOwner;
CumulativeLockInfo storage tokenInfo = cumulativeLockInfo[
userLock.token
];
bool isLpToken = tokenInfo.factory != address(0);
if (isLpToken) {
_userLpLockIds[currentOwner].remove(lockId);
_userLpLockIds[newOwner].add(lockId);
} else {
_userNormalLockIds[currentOwner].remove(lockId);
_userNormalLockIds[newOwner].add(lockId);
}
emit LockOwnerChanged(lockId, currentOwner, newOwner);
}
function renounceLockOwnership(uint256 lockId) external {
transferLockOwnership(lockId, address(0));
}
function _safeTransferFromEnsureExactAmount(
address token,
address sender,
address recipient,
uint256 amount
) internal {
uint256 oldRecipientBalance = IERC20(token).balanceOf(recipient);
IERC20(token).safeTransferFrom(sender, recipient, amount);
uint256 newRecipientBalance = IERC20(token).balanceOf(recipient);
require(
newRecipientBalance - oldRecipientBalance == amount,
"Not enough token was transfered"
);
}
function getTotalLockCount() external view returns (uint256) {
// Returns total lock count, regardless of whether it has been unlocked or not
return _locks.length;
}
function getLockAt(uint256 index) external view returns (Lock memory) {
return _locks[index];
}
function getLockById(uint256 lockId) public view returns (Lock memory) {
return _locks[_getActualIndex(lockId)];
}
function allLpTokenLockedCount() public view returns (uint256) {
return _lpLockedTokens.length();
}
function allNormalTokenLockedCount() public view returns (uint256) {
return _normalLockedTokens.length();
}
function getCumulativeLpTokenLockInfoAt(uint256 index)
external
view
returns (CumulativeLockInfo memory)
{
return cumulativeLockInfo[_lpLockedTokens.at(index)];
}
function getCumulativeNormalTokenLockInfoAt(uint256 index)
external
view
returns (CumulativeLockInfo memory)
{
return cumulativeLockInfo[_normalLockedTokens.at(index)];
}
function getCumulativeLpTokenLockInfo(uint256 start, uint256 end)
external
view
returns (CumulativeLockInfo[] memory)
{
if (end >= _lpLockedTokens.length()) {
end = _lpLockedTokens.length() - 1;
}
uint256 length = end - start + 1;
CumulativeLockInfo[] memory lockInfo = new CumulativeLockInfo[](length);
uint256 currentIndex = 0;
for (uint256 i = start; i <= end; i++) {
lockInfo[currentIndex] = cumulativeLockInfo[_lpLockedTokens.at(i)];
currentIndex++;
}
return lockInfo;
}
function getCumulativeNormalTokenLockInfo(uint256 start, uint256 end)
external
view
returns (CumulativeLockInfo[] memory)
{
if (end >= _normalLockedTokens.length()) {
end = _normalLockedTokens.length() - 1;
}
uint256 length = end - start + 1;
CumulativeLockInfo[] memory lockInfo = new CumulativeLockInfo[](length);
uint256 currentIndex = 0;
for (uint256 i = start; i <= end; i++) {
lockInfo[currentIndex] = cumulativeLockInfo[
_normalLockedTokens.at(i)
];
currentIndex++;
}
return lockInfo;
}
function totalTokenLockedCount() external view returns (uint256) {
return allLpTokenLockedCount() + allNormalTokenLockedCount();
}
function lpLockCountForUser(address user) public view returns (uint256) {
return _userLpLockIds[user].length();
}
function lpLocksForUser(address user)
external
view
returns (Lock[] memory)
{
uint256 length = _userLpLockIds[user].length();
Lock[] memory userLocks = new Lock[](length);
for (uint256 i = 0; i < length; i++) {
userLocks[i] = getLockById(_userLpLockIds[user].at(i));
}
return userLocks;
}
function lpLockForUserAtIndex(address user, uint256 index)
external
view
returns (Lock memory)
{
require(lpLockCountForUser(user) > index, "Invalid index");
return getLockById(_userLpLockIds[user].at(index));
}
function normalLockCountForUser(address user)
public
view
returns (uint256)
{
return _userNormalLockIds[user].length();
}
function normalLocksForUser(address user)
external
view
returns (Lock[] memory)
{
uint256 length = _userNormalLockIds[user].length();
Lock[] memory userLocks = new Lock[](length);
for (uint256 i = 0; i < length; i++) {
userLocks[i] = getLockById(_userNormalLockIds[user].at(i));
}
return userLocks;
}
function normalLockForUserAtIndex(address user, uint256 index)
external
view
returns (Lock memory)
{
require(normalLockCountForUser(user) > index, "Invalid index");
return getLockById(_userNormalLockIds[user].at(index));
}
function totalLockCountForUser(address user)
external
view
returns (uint256)
{
return normalLockCountForUser(user) + lpLockCountForUser(user);
}
function totalLockCountForToken(address token)
external
view
returns (uint256)
{
return _tokenToLockIds[token].length();
}
function getLocksForToken(
address token,
uint256 start,
uint256 end
) public view returns (Lock[] memory) {
if (end >= _tokenToLockIds[token].length()) {
end = _tokenToLockIds[token].length() - 1;
}
uint256 length = end - start + 1;
Lock[] memory locks = new Lock[](length);
uint256 currentIndex = 0;
for (uint256 i = start; i <= end; i++) {
locks[currentIndex] = getLockById(_tokenToLockIds[token].at(i));
currentIndex++;
}
return locks;
}
function _getActualIndex(uint256 lockId) internal view returns (uint256) {
if (lockId < ID_PADDING) {
revert("Invalid lock id");
}
uint256 actualIndex = lockId - ID_PADDING;
require(actualIndex < _locks.length, "Invalid lock id");
return actualIndex;
}
function _parseFactoryAddress(address token)
internal
view
returns (address)
{
address possibleFactoryAddress;
try IUniswapV2Pair(token).factory() returns (address factory) {
possibleFactoryAddress = factory;
} catch {
revert("This token is not a LP token");
}
require(
possibleFactoryAddress != address(0) &&
_isValidLpToken(token, possibleFactoryAddress),
"This token is not a LP token."
);
return possibleFactoryAddress;
}
function _isValidLpToken(address token, address factory)
private
view
returns (bool)
{
IUniswapV2Pair pair = IUniswapV2Pair(token);
address factoryPair = IUniswapV2Factory(factory).getPair(
pair.token0(),
pair.token1()
);
return factoryPair == token;
}
}
Chain explorer3231msChain node82ms