Skip to content

Instantly share code, notes, and snippets.

@coffeexcoin
Last active January 26, 2024 17:27
Show Gist options
  • Save coffeexcoin/289ab6c24d3d83671087bf6f3f6b235d to your computer and use it in GitHub Desktop.
Save coffeexcoin/289ab6c24d3d83671087bf6f3f6b235d to your computer and use it in GitHub Desktop.
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