@@ -10,6 +10,8 @@ import {
1010 getPendingRoot ,
1111 getPendingRootWithTimestamp ,
1212 getTimelock ,
13+ getOwner ,
14+ transferOwnership ,
1315} from "src/lib/rewards.js" ;
1416import { getChain , getRpc , getTransport } from "src/lib/rpc.js" ;
1517import { 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+ }
0 commit comments