Skip to content

Commit

Permalink
Merge pull request #6 from agentcoinorg/nerfzael/migration
Browse files Browse the repository at this point in the history
GeckoV2 and airdrop contract
  • Loading branch information
dOrgJelli authored Jan 31, 2025
2 parents bd843ba + 2c96fc2 commit 59cde79
Show file tree
Hide file tree
Showing 26 changed files with 2,743 additions and 570 deletions.
10 changes: 4 additions & 6 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,12 @@ jobs:
- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly
version: stable

- name: Show Forge version
run: |
forge --version
- name: Run Forge fmt
run: |
forge fmt --check
id: fmt

- name: Run Forge build
run: |
forge build --sizes
Expand All @@ -55,3 +50,6 @@ jobs:
run: |
forge test -vvv
id: test
env:
BASE_RPC_URL: "${{ secrets.BASE_RPC_URL }}"
BASE_UNISWAP_ROUTER: "${{ secrets.BASE_UNISWAP_ROUTER }}"
78 changes: 41 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,37 +1,4 @@
# Agent Keys Contracts

## Context

### Agentcoin TV

[Agentcoin TV](https://agentcoin.tv) is where AI Agents livestream crypto trades and predictions using their own money and logic for all to see. Agent Keys are purchased and used by humans to contribute to the agent's actions. Each livestreaming agent has their own Agent Key.

### Agent Keys

Agent Keys are purchased from a bonding curve smart contract using ETH. Buys and sells of Agent Keys happen through the bonding curve exclusively.

The Agent Key bonding curve is configured as follows:
* Linear Price Curve - The price of agent keys increases linearly with each key sold. Keys start at 0.0002 ETH, and increase by this amount for each key sold.
* Fund Allocations On Buy - For each purchase of an agent key on the bonding curve, the funds used to purchase are split in 3 ways:
* 90% goes to the bonding curve's reserve, used for subsequent sells of agent keys
* 5% is used to grow the agent's treasury, which it uses as working capital
* 5% is sent to the Agentcoin DAO, which aims to grow the network

## Smart Contract Dependencies

The bonding curve implementation used for agent keys is derived [Fairmint's C-Org implementation](https://github.com/Fairmint/c-org). These contracts were audited by Consensys Diligence, and a full report of their findings can be found here: https://diligence.consensys.io/audits/2019/11/fairmint-continuous-securities-offering/

## Development

### Install

```shell
nvm use && nvm install
```

```shell
yarn
```
## Agent Contracts

### Build

Expand All @@ -47,9 +14,46 @@ $ forge test

### Deploy

To deploy AgentKey (Version 1 of the agent token contract)
```shell
$ ./deploy/base-sepolia.sh <BASESCAN_API_KEY>
$ ./deploy/base.sh pk
```

### Deployments
Base Sepolia: https://sepolia.basescan.org/address/0x0D00FE0cd0a5438CCD72bF14690c0783b5f9100F
To deploy the migration contract for Gecko token
```shell
$ ./deploy/gecko-migration-base.sh pk
```

To deploy AgentStaking
```shell
$ ./deploy/agent-staking-base.sh pk
```

### Contracts
#### AgentToken
This contract is the second version of the agent token contract (previous being AgentKey). It is an upgradeable ERC20 token with snapshot functionality.

#### AgentStaking
This contract is used to stake AgentToken tokens. A user can stake or unstake any amount of tokens at any time.
Unstaking tokens will lock the tokens for 1 day before they can be claimed. The user can claim the tokens after the lock period is over.

#### GeckoV2Migrator
This contract is used to migrate the old Gecko token to the new Gecko token.
The old token contract is AgentKey and the new token contract is AgentToken.
It deploys the new Gecko token contract, an airdrop contract for holders of the old token to claim the new token, creates and funds a liquidity pool on Uniswap, and distributes tokens to the AgentCoin DAO, Gecko's cold wallet, and the pool.
There will be 10 million new Gecko tokens minted and distributed as follows:
- 700,000 to the AgentCoin DAO
- 300,000 to the Gecko cold wallet
- 2,500,000 to the airdrop contract for holders of the old Gecko token
- 6,500,000 to the Uniswap pool

##### Migration process
1. Deploy the GeckoV2Migrator contract
2. Call the stopAndTransferReserve function on the old Gecko token contract to stop the token and transfer the reserves to the new contract
3. Call the migrate function on the GeckoV2Migrator contract to start the migration process
2. and 3. will be done within the same transaction by the DAO's Gnosis Safe

#### AirdopClaim
This contract is used to claim the new Gecko token for holders of the old Gecko token. The contract is funded by the GeckoV2Migrator contract and the user can claim the new token by calling the claim function.
Anyone can call the claim function for any address, but that address can only claim once.

4 changes: 4 additions & 0 deletions airdrop/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
AIRDROP_CONTRACT_ADDRESS=
ALCHEMY_API_KEY=
WALLET_PRIVATE_KEY=
BATCH_SIZE=100
1 change: 1 addition & 0 deletions airdrop/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
holders.txt
1 change: 1 addition & 0 deletions airdrop/.nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
v20.12.2
14 changes: 14 additions & 0 deletions airdrop/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Gecko V1 to V2 Migration Airdrop

Steps to run the airdrop script:
1. Install dependencies
```shell
$ yarn install
```

2. Copy and rename the `.env.example` file to `.env` and fill in the required environment variables
3. Create a `holders.txt` file with the addresses of the holders of the old Gecko token
4. Run the airdrop script
```shell
$ yarn start
```
14 changes: 14 additions & 0 deletions airdrop/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "airdrop",
"version": "1.0.0",
"license": "MIT",
"type": "module",
"scripts": {
"start": "node run.js"
},
"dependencies": {
"axios": "1.7.9",
"dotenv": "16.4.7",
"ethers": "6.13.4"
}
}
83 changes: 83 additions & 0 deletions airdrop/run.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import * as ethers from "ethers";
import fs from "fs";
import dotenv from "dotenv";

dotenv.config();

const AIRDROP_CONTRACT_ADDRESS = process.env.AIRDROP_CONTRACT_ADDRESS;
const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY;
const WALLET_PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY;

if (!AIRDROP_CONTRACT_ADDRESS) {
throw new Error('AIRDROP_CONTRACT_ADDRESS is required');
}
if (!ALCHEMY_API_KEY) {
throw new Error('ALCHEMY_API_KEY is required');
}
if (!WALLET_PRIVATE_KEY) {
throw new Error('WALLET_PRIVATE_KEY is required');
}

const BATCH_SIZE = process.env.BATCH_SIZE
? parseInt(process.env.BATCH_SIZE)
: 100;

const provider = new ethers.JsonRpcProvider(`https://base-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}`);

const contractABI = [
'function multiClaim(address[] recipients) external'
];

const contract = new ethers.Contract(AIRDROP_CONTRACT_ADDRESS, contractABI, provider);

function getTokenHolders() {
const text = fs.readFileSync('holders.txt', 'utf-8');
const addresses = text.split('\n').map((holder) => holder.trim());

for (const address of addresses) {
if (!ethers.isAddress(address)) {
throw new Error(`Invalid address: ${address}`);
}
}

return addresses;
}

async function multiClaimForBatch(addresses) {
const wallet = new ethers.Wallet(WALLET_PRIVATE_KEY, provider);

const contractWithSigner = contract.connect(wallet);

try {
console.log(`Claiming for batch of ${addresses.length} addresses...`);
const tx = await contractWithSigner.multiClaim(addresses);
await tx.wait(1);
console.log('Batch claim successful:', tx.hash);
} catch (error) {
console.error('Error claiming for batch:', error);
}
}

async function processClaims() {
const holders = getTokenHolders();

if (holders.length === 0) {
console.log('No token holders found.');
return;
}

let batch = [];
for (let i = 0; i < holders.length; i++) {
const holder = holders[i];
batch.push(holder);

if (batch.length === BATCH_SIZE || i === holders.length - 1) {
await multiClaimForBatch(batch);
batch = [];
}
}

console.log('All claims processed.');
}

processClaims().catch(console.error).then(() => process.exit(0));
122 changes: 122 additions & 0 deletions airdrop/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@adraffy/[email protected]":
version "1.10.1"
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz#63430d04bd8c5e74f8d7d049338f1cd9d4f02069"
integrity sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==

"@noble/[email protected]":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35"
integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==
dependencies:
"@noble/hashes" "1.3.2"

"@noble/[email protected]":
version "1.3.2"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39"
integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==

"@types/[email protected]":
version "22.7.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b"
integrity sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==
dependencies:
undici-types "~6.19.2"

[email protected]:
version "4.0.0-beta.5"
resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873"
integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==

asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==

[email protected]:
version "1.7.9"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
integrity sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==
dependencies:
follow-redirects "^1.15.6"
form-data "^4.0.0"
proxy-from-env "^1.1.0"

combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"

delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==

dotenv@^16.4.7:
version "16.4.7"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.4.7.tgz#0e20c5b82950140aa99be360a8a5f52335f53c26"
integrity sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==

[email protected]:
version "6.13.4"
resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.4.tgz#bd3e1c3dc1e7dc8ce10f9ffb4ee40967a651b53c"
integrity sha512-21YtnZVg4/zKkCQPjrDj38B1r4nQvTZLopUGMLQ1ePU2zV/joCfDC3t3iKQjWRzjjjbzR+mdAIoikeBRNkdllA==
dependencies:
"@adraffy/ens-normalize" "1.10.1"
"@noble/curves" "1.2.0"
"@noble/hashes" "1.3.2"
"@types/node" "22.7.5"
aes-js "4.0.0-beta.5"
tslib "2.7.0"
ws "8.17.1"

follow-redirects@^1.15.6:
version "1.15.9"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1"
integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==

form-data@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48"
integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"

[email protected]:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==

mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"

proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==

[email protected]:
version "2.7.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==

undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==

[email protected]:
version "8.17.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b"
integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==
25 changes: 25 additions & 0 deletions deploy/agent-staking-base.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
if [[ $1 = "pk" ]]; then
export $(cat .env | xargs) && \
forge script ./script/DeployAgentStaking.s.sol \
--rpc-url $BASE_RPC_URL \
--broadcast \
-g 200 \
--force \
--verify \
--verifier-url https://api.basescan.org/api \
--etherscan-api-key $BASESCAN_API_KEY \
--interactives 1 \
--slow
else
export $(cat .env | xargs) && \
forge script ./script/DeployAgentStaking.s.sol \
--rpc-url $BASE_RPC_URL \
--broadcast \
-g 200 \
--force \
--verify \
--verifier-url https://api.basescan.org/api \
--etherscan-api-key $BASESCAN_API_KEY \
--account $FORGE_ACCOUNT \
--slow
fis
Loading

0 comments on commit 59cde79

Please sign in to comment.