Skip to content

Commit b22ca55

Browse files
committed
noot
1 parent 2c451a2 commit b22ca55

File tree

2 files changed

+246
-1
lines changed

2 files changed

+246
-1
lines changed

src/cmd/rewards.ts

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import {
1010
getPendingRoot,
1111
getPendingRootWithTimestamp,
1212
getTimelock,
13+
getOwner,
14+
transferOwnership,
1315
} from "src/lib/rewards.js";
1416
import { getChain, getRpc, getTransport } from "src/lib/rpc.js";
1517
import { MorphoRewardProgram } from "src/lib/types.js";
@@ -731,3 +733,213 @@ export class RepublishRoot extends Command {
731733
);
732734
}
733735
}
736+
737+
export class ShowRewardInfo extends Command {
738+
static paths = [["reward", "info"]];
739+
740+
id = Option.String({ required: false });
741+
dir = Option.String("--dir", "chains");
742+
743+
async execute() {
744+
const { reward } = await selectReward(this.dir, this.id, "show info");
745+
if (!reward) {
746+
throw new Error(`reward ${this.id} not found. try 'reward list'`);
747+
}
748+
749+
const { chain, publicClient } = getWalletInfo(reward.chainId);
750+
751+
if (!("urdFactory" in chain.morpho)) {
752+
throw new Error(`No urdFactory for chain ${chain.id}.'`);
753+
}
754+
755+
console.log("=== Reward Campaign Information ===\n");
756+
757+
console.log(`📋 Campaign Details:`);
758+
console.log(` ID: ${reward.id}`);
759+
console.log(` Name: ${reward.name || "No name specified"}`);
760+
console.log(` Type: ${reward.type}`);
761+
console.log(` Chain ID: ${reward.chainId}`);
762+
console.log(` Production: ${reward.production ? "Yes" : "No"}`);
763+
console.log(` Finished: ${reward.finished ? "Yes" : "No"}`);
764+
765+
console.log(`\n📍 Addresses:`);
766+
console.log(` URD Contract: ${reward.urdAddress}`);
767+
console.log(` Reward Token: ${reward.reward_token}`);
768+
if (reward.vault) {
769+
console.log(` Vault: ${reward.vault}`);
770+
}
771+
if (reward.market) {
772+
console.log(` Market: ${reward.market}`);
773+
}
774+
775+
console.log(`\n💰 Reward Details:`);
776+
console.log(` Total Amount: ${reward.reward_amount}`);
777+
const rewardBigInt = BigInt(reward.reward_amount);
778+
const rewardFormatted = (Number(rewardBigInt) / 1e18).toFixed(2);
779+
console.log(` Total Amount (formatted): ${rewardFormatted}`);
780+
781+
console.log(`\n⏱️ Timeline:`);
782+
const startDate = new Date(reward.start_timestamp * 1000);
783+
const endDate = new Date(reward.end_timestamp * 1000);
784+
console.log(` Start: ${startDate.toISOString()} (${reward.start_timestamp})`);
785+
console.log(` End: ${endDate.toISOString()} (${reward.end_timestamp})`);
786+
787+
const now = Date.now() / 1000;
788+
if (now < reward.start_timestamp) {
789+
const daysUntilStart = Math.ceil((reward.start_timestamp - now) / 86400);
790+
console.log(` Status: Not started (starts in ${daysUntilStart} days)`);
791+
} else if (now > reward.end_timestamp) {
792+
const daysSinceEnd = Math.floor((now - reward.end_timestamp) / 86400);
793+
console.log(` Status: Ended (${daysSinceEnd} days ago)`);
794+
} else {
795+
const elapsed = now - reward.start_timestamp;
796+
const total = reward.end_timestamp - reward.start_timestamp;
797+
const percentage = (elapsed / total) * 100;
798+
const daysRemaining = Math.ceil((reward.end_timestamp - now) / 86400);
799+
console.log(` Status: Active (${percentage.toFixed(2)}% complete, ${daysRemaining} days remaining)`);
800+
}
801+
802+
console.log(`\n🔧 Technical Details:`);
803+
console.log(` Salt: ${reward.salt}`);
804+
805+
try {
806+
const pendingRoot = await getPendingRoot(
807+
publicClient,
808+
getAddress(reward.urdAddress),
809+
);
810+
811+
const pendingRootData = await getPendingRootWithTimestamp(
812+
publicClient,
813+
getAddress(reward.urdAddress),
814+
);
815+
816+
const timelockPeriod = await getTimelock(
817+
publicClient,
818+
getAddress(reward.urdAddress),
819+
);
820+
821+
const owner = await getOwner(
822+
publicClient,
823+
getAddress(reward.urdAddress),
824+
);
825+
826+
console.log(`\n📊 On-chain State:`);
827+
console.log(` Owner: ${owner}`);
828+
console.log(` Pending Root: ${pendingRoot}`);
829+
console.log(` Timelock Period: ${timelockPeriod.toString()} seconds (${Number(timelockPeriod) / 3600} hours)`);
830+
831+
if (pendingRoot !== zeroHash) {
832+
const validAtTimestamp = Number(pendingRootData.timestamp);
833+
const currentTime = Math.floor(Date.now() / 1000);
834+
835+
if (currentTime >= validAtTimestamp) {
836+
console.log(` Root Status: ✅ Ready to accept`);
837+
} else {
838+
const timeRemaining = validAtTimestamp - currentTime;
839+
const hoursRemaining = Math.floor(timeRemaining / 3600);
840+
const minutesRemaining = Math.floor((timeRemaining % 3600) / 60);
841+
console.log(` Root Status: ⏳ In timelock (${hoursRemaining}h ${minutesRemaining}m remaining)`);
842+
}
843+
844+
if (pendingRootData.ipfs && pendingRootData.ipfs !== zeroHash) {
845+
console.log(` IPFS Hash: ${pendingRootData.ipfs}`);
846+
}
847+
} else {
848+
console.log(` Root Status: No pending root`);
849+
}
850+
851+
} catch (error) {
852+
console.log(`\n⚠️ Could not fetch on-chain data: ${error instanceof Error ? error.message : error}`);
853+
}
854+
855+
console.log(`\n🔗 Links:`);
856+
console.log(` Explorer: https://maizenet-explorer.usecorn.com/address/${reward.urdAddress}`);
857+
}
858+
}
859+
860+
export class TransferOwner extends Command {
861+
static paths = [["reward", "transfer-owner"]];
862+
863+
id = Option.String({ required: false });
864+
newOwner = Option.String();
865+
dir = Option.String("--dir", "chains");
866+
867+
async execute() {
868+
const { reward } = await selectReward(this.dir, this.id, "transfer ownership");
869+
if (!reward) {
870+
throw new Error(`reward ${this.id} not found. try 'reward list'`);
871+
}
872+
873+
if (!this.newOwner || !isAddress(this.newOwner)) {
874+
throw new Error("new-owner must be a valid address");
875+
}
876+
877+
const { chain, publicClient, walletClient } = getWalletInfo(reward.chainId);
878+
879+
if (!("urdFactory" in chain.morpho)) {
880+
throw new Error(`No urdFactory for chain ${chain.id}.'`);
881+
}
882+
883+
// Get current owner
884+
const currentOwner = await getOwner(
885+
publicClient,
886+
getAddress(reward.urdAddress),
887+
);
888+
889+
console.log(`\n⚠️ WARNING: Ownership Transfer`);
890+
console.log(`Campaign: ${reward.id} (${reward.name || "no name"})`);
891+
console.log(`URD Contract: ${reward.urdAddress}`);
892+
console.log(`Current Owner: ${currentOwner}`);
893+
console.log(`New Owner: ${this.newOwner}`);
894+
console.log(`\n🚨 This action is IRREVERSIBLE!`);
895+
896+
const answer = await confirm({
897+
message: `Are you absolutely sure you want to transfer ownership to ${this.newOwner}?`,
898+
default: false,
899+
});
900+
901+
if (!answer) {
902+
console.log("Transfer cancelled");
903+
return;
904+
}
905+
906+
// Double confirmation for safety
907+
const secondAnswer = await confirm({
908+
message: `FINAL CONFIRMATION: Transfer ownership of ${reward.id} to ${this.newOwner}?`,
909+
default: false,
910+
});
911+
912+
if (!secondAnswer) {
913+
console.log("Transfer cancelled");
914+
return;
915+
}
916+
917+
try {
918+
const txnhash = await transferOwnership(
919+
publicClient,
920+
walletClient,
921+
getAddress(reward.urdAddress),
922+
getAddress(this.newOwner),
923+
);
924+
925+
console.log(`\n✅ Ownership transferred successfully!`);
926+
console.log(`Transaction hash: ${txnhash}`);
927+
console.log(`New owner: ${this.newOwner}`);
928+
929+
// Verify the transfer
930+
const newOwnerVerified = await getOwner(
931+
publicClient,
932+
getAddress(reward.urdAddress),
933+
);
934+
935+
if (newOwnerVerified.toLowerCase() === this.newOwner.toLowerCase()) {
936+
console.log(`✅ Ownership transfer verified on-chain`);
937+
} else {
938+
console.log(`⚠️ Warning: Could not verify ownership transfer. Please check manually.`);
939+
}
940+
} catch (error) {
941+
console.log(`\n❌ Transfer failed: ${error instanceof Error ? error.message : error}`);
942+
throw error;
943+
}
944+
}
945+
}

src/lib/rewards.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ export const getPendingRootWithTimestamp = async (
7070
})
7171
return {
7272
root: result[0],
73-
ipfs: result[1],
73+
ipfs: result[1],
7474
timestamp: result[2]
7575
}
7676
}
@@ -88,6 +88,19 @@ export const getTimelock = async (
8888
return result
8989
}
9090

91+
export const getOwner = async (
92+
client: PublicClient,
93+
urdAddress: Address,
94+
) => {
95+
const result = await client.readContract({
96+
address: urdAddress,
97+
abi: parseAbi([`function owner() external view returns (address)`]),
98+
functionName: "owner",
99+
args: [],
100+
})
101+
return result
102+
}
103+
91104
export const updateRewardRoot = async (
92105
client: PublicClient,
93106
payer: WalletClient,
@@ -130,6 +143,26 @@ export const acceptRewardRoot = async (
130143
return txn
131144
}
132145

146+
export const transferOwnership = async (
147+
client: PublicClient,
148+
payer: WalletClient,
149+
urdAddress: Address,
150+
newOwner: Address,
151+
) => {
152+
if(!payer.account) throw new Error("payer account not found")
153+
154+
const {request} = await client.simulateContract({
155+
account: payer.account,
156+
address: urdAddress,
157+
abi: parseAbi([`function setOwner(address newOwner) external`]),
158+
functionName: "setOwner",
159+
args: [newOwner],
160+
})
161+
const txn = await payer.writeContract(request)
162+
await client.waitForTransactionReceipt({hash: txn})
163+
return txn
164+
}
165+
133166
export const createRewards = async (
134167
client: PublicClient,
135168
payer: WalletClient,

0 commit comments

Comments
 (0)