Last active
January 26, 2024 17:27
-
-
Save coffeexcoin/289ab6c24d3d83671087bf6f3f6b235d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Alchemy, Network } from "alchemy-sdk"; | |
import { Wallet, Contract, providers, utils} from "ethers"; | |
const Contracts = { | |
MegaPlunk: "0x51ae7f53fA2cE21b06B89ABB5ddC83FfDB868423", | |
Frontlines: "0x0F9B1418694ADAEe240Cb0d76B805d197da5ae8a", | |
Q00tants: "0x9F7C5D43063e3ECEb6aE43A22b669BB01fD1039A" | |
}; | |
async function getRektCorns() { | |
// Alchemy API setup, used to get owned tokens | |
const alchemy = new Alchemy({ | |
apiKey: process.env.ALCHEMY_KEY, | |
network: Network.ETH_GOERLI | |
}) | |
// Create an alchemy provider for RPC calls to goerli | |
const provider = new providers.AlchemyProvider("goerli", process.env.ALCHEMY_KEY); | |
// Create a signer using the provided private key | |
const signer = new Wallet(process.env.GOERLI_PRIVATE_KEY as string, provider); | |
// The interface for the frontlines contract | |
const frontLinesInterface = new utils.Interface([ | |
"function mine(uint256) external", | |
]); | |
// Attach to signer to the interface at the specified address so we can call it later | |
const frontLinesContract = new Contract(Contracts.Frontlines, frontLinesInterface, signer); | |
let rescueTokens: number[] = []; | |
// Target "from" value is the current owner of plunk. This is the megaplunk contract | |
// Convert the address to a binary string and zero pad it to 160 bits | |
const target = BigInt(Contracts.MegaPlunk).toString(2).padStart(160, "0"); | |
// Get an interator for all owned q00tants from the alchemy NFT API | |
const ownedQ00tantsIterator = alchemy.nft.getNftsForOwnerIterator(signer.address, { | |
contractAddresses: [Contracts.Q00tants] | |
}); | |
// Asynchronously iterate over all owned q00tants | |
for await (const q00t of ownedQ00tantsIterator) { | |
const tokenId = Number(q00t.tokenId) | |
// The slot for the frontlines miner mapping (uint256 => uint256) is the 4th slot | |
// The layout for each value is found with `keccak256(abi.encode(tokenId, 4))` | |
const slotId = utils.keccak256(utils.defaultAbiCoder.encode(["uint256", "uint256"], [tokenId, 4])) | |
// Get the raw storage for the next time the specified token id can mine | |
const nextMineRawStorage = await provider.getStorageAt(Contracts.Frontlines, slotId) | |
const nextMine = BigInt(nextMineRawStorage); | |
const now = Date.now() / 1000; | |
if (nextMine > now) { | |
console.log(`Token ${tokenId} can't mine yet`); | |
} else { | |
// q00tant can mine glitter, add it to the list of tokens to attempt rescue | |
rescueTokens.push(tokenId); | |
} | |
} | |
const getStatus = async () => { | |
const currentVal = await provider.getStorageAt(Contracts.MegaPlunk, 0); | |
const current = BigInt(currentVal).toString(2).padStart(160, "0"); | |
const remaining = (BigInt(Contracts.MegaPlunk) ^ BigInt(currentVal)).toString(2).padStart(160, "0"); | |
return [current, remaining] | |
} | |
const logStatus = (target: string, current: string, remaining: string) => { | |
console.log(`Target "from" value: ${target}`) | |
console.log(`Current "from" value: ${current}`); | |
console.log(`Remaining bits: ${remaining}`) | |
} | |
if (rescueTokens.length === 0) { | |
const [current, remaining] = await getStatus(); | |
logStatus(target, current, remaining); | |
console.log("All tokens are on cooldown"); | |
return; | |
} | |
for (const rescueToken of rescueTokens) { | |
const [current, remaining] = await getStatus(); | |
logStatus(target, current, remaining); | |
// iterate the 160 bits of the target address left to right (highest order bit to lowest) | |
// and compare to the current storage value to find bits that need to be flipped. | |
for (let i = 0; i < 160; i++) { | |
if (target[i] === "1" && target[i] !== current[i]) { | |
console.log(`Need to flip bit ${159-i}`) | |
// encode as a uint256 hex string | |
const data = `0x${utils.hexlify(159-i).slice(2).padStart(64, "0")}`; | |
console.log(`Mining glitter with q00tant ${rescueToken}`); | |
// Mine glitter from the frontlines contract with the specified token id | |
const mineTx = await frontLinesContract.mine(rescueToken); | |
// need to wait for this to confirm before sending the rescue tx | |
await mineTx.wait(); | |
console.log(`Sending rescue tx with data ${data}`); | |
// Send a transaction to megaplunk - the data is the position of the bit to flip | |
// in the "from" address. Once this matches the target address, the megaplunk contract | |
// will automatically transfer out Plunk and end the epoch. | |
const rescueTx = await signer.sendTransaction({ | |
to: Contracts.MegaPlunk, | |
data | |
}) | |
// wait for this to confirm, one transaction allowed per block | |
await rescueTx.wait(); | |
break; | |
} | |
} | |
} | |
} | |
getRektCorns(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment