diff --git a/scripts/approve-superproposal.ts b/scripts/approve-superproposal.ts index d47f48f..e34b45c 100644 --- a/scripts/approve-superproposal.ts +++ b/scripts/approve-superproposal.ts @@ -25,9 +25,9 @@ async function main() { console.log('Creating super proposal + sub proposal on subPod'); try { if (isMember) { - await superPod.proposeBurnMemberFromPod(subPod, dummyAccount, walletOne); + await superPod.burnMemberFromSubPod(subPod, dummyAccount, walletOne); } else { - await superPod.proposeMintMemberFromPod(subPod, dummyAccount, walletOne); + await superPod.mintMemberFromSubPod(subPod, dummyAccount, walletOne); } } catch (err) { console.log(err); diff --git a/scripts/mint-from-admin.ts b/scripts/mint-from-admin.ts new file mode 100644 index 0000000..6077742 --- /dev/null +++ b/scripts/mint-from-admin.ts @@ -0,0 +1,43 @@ +import { getPod } from '../src'; +import { adminPodAddress, dummyAccount, subPodAddress } from '../env.json'; +import { setup, sleep } from './utils'; + +async function main() { + const { walletOne, walletTwo } = setup(); + + // Get the pod we're working with + + const adminPod = await getPod(adminPodAddress); + const subPod = await getPod(subPodAddress) + + if ((await adminPod.getProposals({ queued: true }))[0].status !== 'executed') { + throw new Error( + 'Super pod had an active/queued transaction. This script expects no enqueued transactions', + ); + } + + console.log('subPod.admin', subPod.admin); + + // We mint/burn the dummy account based on whether its a member or not. + const isMember = await subPod.isMember(dummyAccount); + if (isMember) { + await subPod.burnMemberFromAdminPod(adminPod, dummyAccount, walletOne); + } else { + await subPod.mintMemberFromAdminPod(adminPod, dummyAccount, walletOne); + } + + const proposal = (await adminPod.getProposals())[0]; + + await proposal.approve(walletTwo); + await proposal.executeApprove(walletTwo); + + // // Let gnosis catch up. + // await sleep(5000); + + // if (proposal.status !== 'executed') throw new Error('Proposal status not right'); + // const refetchProposal = (await adminPod.getProposals())[0]; + // if (!refetchProposal.safeTransaction.isExecuted) + // throw new Error('Proposal not executed according to gnosis'); +} + +main(); diff --git a/scripts/reject-superproposal.ts b/scripts/reject-superproposal.ts index f1a6cec..66fa144 100644 --- a/scripts/reject-superproposal.ts +++ b/scripts/reject-superproposal.ts @@ -26,10 +26,10 @@ async function main() { try { if (isMember) { console.log('Burning'); - await superPod.proposeBurnMemberFromPod(subPod, dummyAccount, walletOne); + await superPod.burnMemberFromSubPod(subPod, dummyAccount, walletOne); } else { console.log('Minting'); - await superPod.proposeMintMemberFromPod(subPod, dummyAccount, walletOne); + await superPod.mintMemberFromSubPod(subPod, dummyAccount, walletOne); } } catch (err) { console.log(err); diff --git a/src/Pod.ts b/src/Pod.ts index e71d9d7..31e3be7 100644 --- a/src/Pod.ts +++ b/src/Pod.ts @@ -359,7 +359,6 @@ export default class Pod { /** * Checks if given address is a member of any subpods. - * * Returns false if the user is a member of **this** pod, but not any sub pods * * @param address @@ -378,13 +377,15 @@ export default class Pod { /** * Mints member to this pod. - * @throws if signer is not admin TODO + * @throws if signer is not admin */ mintMember = async ( newMember: string, signer: ethers.Signer, ): Promise => { checkAddress(newMember); + const signerAddress = await signer.getAddress(); + if (!this.isAdmin(signerAddress)) throw new Error('Signer was not admin'); try { return getContract('MemberToken', signer).mint(newMember, this.id, ethers.constants.HashZero); } catch (err) { @@ -394,13 +395,15 @@ export default class Pod { /** * Burns member from this pod. - * @throws If signer is not admin TODO + * @throws If signer is not admin */ burnMember = async ( memberToBurn: string, signer: ethers.Signer, ): Promise => { checkAddress(memberToBurn); + const signerAddress = await signer.getAddress(); + if (!this.isAdmin(signerAddress)) throw new Error('Signer was not admin'); try { return getContract('MemberToken', signer).burn(memberToBurn, this.id); } catch (err) { @@ -498,17 +501,23 @@ export default class Pod { }; /** - * Creates a proposal on an external pod to mint a new member to this pod. - * @param externalPodIdentifier - The Pod object, pod ID or pod safe address of either the admin pod, or a subpod of this pod. + * Creates a proposal on this pod to mint a new member. Also creates + approves a corresponding + * sub proposal on the sub pod. + * + * After the proposal is created, other sub pods can interact with the super proposal + * by fetching the super proposal object from the super pod and calling + * Proposal.approveFromSubPod/rejectFromSubPod + * + * @param subPodIdentifier - The Pod object, pod ID or pod safe address of either the admin pod, or a subpod of this pod. * @param newMember - Member to mint * @param signer - Signer of external pod member * @throws If newMember is already part of this pod - * @throws If externalPodIdentifier does not correlate to existing pod - * @throws If externalPodIdentifier is not the admin or subpod of this pod + * @throws If subPodIdentifier does not correlate to existing pod + * @throws If subPodIdentifier is not the admin or subpod of this pod * @throws If signer is not a member of external pod */ - proposeMintMemberFromPod = async ( - externalPodIdentifier: Pod | string | number, + mintMemberFromSubPod = async ( + subPodIdentifier: Pod | string | number, newMember: string, signer: ethers.Signer, ) => { @@ -516,23 +525,20 @@ export default class Pod { throw new Error(`Address ${newMember} is already in this pod`); } - let externalPod: Pod; - if (externalPodIdentifier instanceof Pod) externalPod = externalPodIdentifier; + let subPod: Pod; + if (subPodIdentifier instanceof Pod) subPod = subPodIdentifier; else { - externalPod = await new Pod(externalPodIdentifier); + subPod = await new Pod(subPodIdentifier); } - if (!externalPod) - throw new Error(`Could not find a pod with identifier ${externalPodIdentifier}`); + if (!subPod) throw new Error(`Could not find a pod with identifier ${subPodIdentifier}`); - // External pod must be the admin or a subpod of this pod. - if (!(this.isAdmin(externalPod.safe) || (await this.isMember(externalPod.safe)))) { - throw new Error( - `Pod ${externalPod.safe} must be the admin or a subpod of this pod to make proposals`, - ); + // External pod must be the subpod of this pod. + if (!(await this.isMember(subPod.safe))) { + throw new Error(`Pod ${subPod.safe} must be a subpod of this pod to make proposals`); } const signerAddress = await signer.getAddress(); - if (!(await externalPod.isMember(signerAddress))) + if (!(await subPod.isMember(signerAddress))) throw new Error(`Signer ${signerAddress} was not a member of the external pod`); // Tells MemberToken to mint a new token for this pod to newMember. @@ -547,12 +553,72 @@ export default class Pod { // Create a safe transaction on this pod, sent from the signer await createNestedProposal( { - sender: externalPod.safe, + sender: subPod.safe, safe: this.safe, to: memberTokenAddress, data, }, - externalPod, + subPod, + signer, + ); + } catch (err) { + throw new Error(err); + } + }; + + /** + * Creates a proposal on the admin pod to mint a new member to this pod. + * + * @param adminPodIdentifier - The Pod object, pod ID or pod safe address of the admin pod of this pod + * @param newMember - Member to mint + * @param signer - Signer of external pod member + * @throws If newMember is already part of this pod + * @throws If adminPodIdentifier does not correlate to existing pod + * @throws If adminPodIdentifier is not the admin of this pod + * @throws If signer is not a member of external pod + */ + mintMemberFromAdminPod = async ( + adminPodIdentifier: Pod | string | number, + newMember: string, + signer: ethers.Signer, + ) => { + if (await this.isMember(newMember)) { + throw new Error(`Address ${newMember} is already in this pod`); + } + + let adminPod: Pod; + if (adminPodIdentifier instanceof Pod) adminPod = adminPodIdentifier; + else { + adminPod = await new Pod(adminPodIdentifier); + } + if (!adminPod) throw new Error(`Could not find a pod with identifier ${adminPodIdentifier}`); + + // External pod must be the admin of this pod + if (!this.isAdmin(adminPod.safe)) { + throw new Error(`Pod ${adminPod.safe} must be the admin of this pod to make proposals`); + } + + const signerAddress = await signer.getAddress(); + if (!(await adminPod.isMember(signerAddress))) + throw new Error(`Signer ${signerAddress} was not a member of the admin pod`); + + // Tells MemberToken to mint a new token for this pod to newMember. + const data = encodeFunctionData('MemberToken', 'mint', [ + ethers.utils.getAddress(newMember), + this.id, + ethers.constants.HashZero, + ]); + + const { address: memberTokenAddress } = getContract('MemberToken', signer); + try { + // Create a safe transaction on the admin pod + await createSafeTransaction( + { + sender: signerAddress, + safe: adminPod.safe, + to: memberTokenAddress, + data, + }, signer, ); } catch (err) { @@ -596,17 +662,22 @@ export default class Pod { }; /** - * Creates a proposal on an external pod to burn a new member from this pod. - * @param externalPodIdentifier - The Pod object, pod ID or pod safe address of either the admin pod, or a subpod of this pod. + * Creates a proposal on this pod to burn an existing member. Also creates + approves a corresponding + * sub proposal on the sub pod. + * + * After the proposal is created, other sub pods can interact with the super proposal + * by fetching the Proposal object and calling Proposal.approveFromSubPod/rejectFromSubPod + * + * @param subPodIdentifier - The Pod object, pod ID or pod safe address of either the admin pod, or a subpod of this pod. * @param memberToBurn - Member to burn * @param signer - Signer of external pod member * @throws If memberToBurn is not part of this pod - * @throws If externalPodIdentifier is not an existing pod - * @throws If externalPodIdentifier is not the admin or subpod of this pod + * @throws If subPodIdentifier is not an existing pod + * @throws If subPodIdentifier is not the admin or subpod of this pod * @throws If Signer is not a member of the external pod */ - proposeBurnMemberFromPod = async ( - externalPodIdentifier: Pod | string | number, + burnMemberFromSubPod = async ( + subPodIdentifier: Pod | string | number, memberToBurn: string, signer: ethers.Signer, ) => { @@ -614,24 +685,23 @@ export default class Pod { throw new Error(`Address ${memberToBurn} is not in this pod`); } - let externalPod: Pod; - if (externalPodIdentifier instanceof Pod) externalPod = externalPodIdentifier; + let subPod: Pod; + if (subPodIdentifier instanceof Pod) subPod = subPodIdentifier; else { - externalPod = await new Pod(externalPodIdentifier); + subPod = await new Pod(subPodIdentifier); } - if (!externalPod) - throw new Error(`Could not find a pod with identifier ${externalPodIdentifier}`); + if (!subPod) throw new Error(`Could not find a pod with identifier ${subPodIdentifier}`); // External pod must be the admin or a subpod of this pod. - if (!(this.isAdmin(externalPod.safe) || (await this.isMember(externalPod.safe)))) { + if (!(await this.isMember(subPod.safe))) { throw new Error( - `Pod ${externalPod.safe} must be the admin or a subpod of this pod to make proposals`, + `Pod ${subPod.safe} must be the admin or a subpod of this pod to make proposals`, ); } const signerAddress = await signer.getAddress(); - if (!(await externalPod.isMember(signerAddress))) - throw new Error(`Signer ${signerAddress} was not a member of the external pod`); + if (!(await subPod.isMember(signerAddress))) + throw new Error(`Signer ${signerAddress} was not a member of the sub pod`); // Tells MemberToken to mint a new token for this pod to newMember. const data = encodeFunctionData('MemberToken', 'burn', [ @@ -644,12 +714,12 @@ export default class Pod { // Create a safe transaction on this pod, sent from the admin pod await createNestedProposal( { - sender: externalPod.safe, + sender: subPod.safe, safe: this.safe, to: memberTokenAddress, data, }, - externalPod, + subPod, signer, ); } catch (err) { @@ -657,6 +727,66 @@ export default class Pod { } }; + /** + * Creates a proposal on the admin pod to burn a member from this pod + * @param adminPodIdentifier - The Pod object, pod ID or pod safe address of either the admin pod, or a subpod of this pod. + * @param memberToBurn - Member to burn + * @param signer - Signer of external pod member + * @throws If memberToBurn is not part of this pod + * @throws If adminPodIdentifier is not an existing pod + * @throws If adminPodIdentifier is not the admin or subpod of this pod + * @throws If Signer is not a member of the external pod + */ + burnMemberFromAdminPod = async ( + adminPodIdentifier: Pod | string | number, + memberToBurn: string, + signer: ethers.Signer, + ) => { + if (!(await this.isMember(memberToBurn))) { + throw new Error(`Address ${memberToBurn} is not in this pod`); + } + + let adminPod: Pod; + if (adminPodIdentifier instanceof Pod) adminPod = adminPodIdentifier; + else { + adminPod = await new Pod(adminPodIdentifier); + } + if (!adminPod) throw new Error(`Could not find a pod with identifier ${adminPodIdentifier}`); + + // External pod must be the admin or a subpod of this pod. + if (!this.isAdmin(adminPod.safe)) { + throw new Error( + `Pod ${adminPod.safe} must be the admin or a subpod of this pod to make proposals`, + ); + } + + const signerAddress = await signer.getAddress(); + if (!(await adminPod.isMember(signerAddress))) + throw new Error(`Signer ${signerAddress} was not a member of the external pod`); + + // Tells MemberToken to mint a new token for this pod to newMember. + const data = encodeFunctionData('MemberToken', 'burn', [ + ethers.utils.getAddress(memberToBurn), + this.id, + ]); + + const { address: memberTokenAddress } = getContract('MemberToken', signer); + try { + // Create a safe transaction on the admin pod, sent from the signer + await createSafeTransaction( + { + sender: signerAddress, + safe: adminPod.safe, + to: memberTokenAddress, + data, + }, + signer, + ); + } catch (err) { + throw new Error(err.message); + } + }; + /** * Creates a proposal to transfer membership from a subpod * @param subPodIdentifier - Pod, Pod ID or safe address @@ -666,7 +796,7 @@ export default class Pod { * @throws If subPodIdentifier does not exist * @throws If Signer is not a member of this sub pod */ - proposeTransferMembershipFromSubPod = async ( + transferMembershipFromSubPod = async ( subPodIdentifier: Pod | string | number, addressToTransferTo: string, signer: ethers.Signer, @@ -703,15 +833,14 @@ export default class Pod { const { address: memberTokenAddress } = getContract('MemberToken', signer); try { - // Create a safe transaction on this pod, sent from the admin pod - // TODO: Gotta update to make this work. - await createSafeTransaction( + await createNestedProposal( { sender: subPod.safe, safe: this.safe, to: memberTokenAddress, data, }, + subPod, signer, ); } catch (err) { @@ -721,8 +850,9 @@ export default class Pod { /** * Creates proposal to transfer the admin role from the admin pod + * * @param adminPodIdentifier - Pod ID, safe address, or ENS name of admin pod - * @param addressToTransferTo - Address that will receive admin roll + * @param addressToTransferTo - Address that will receive admin role * @param signer - Signer of admin pod member * @throws If addressToTransferTo is already the pod admin * @throws If adminPodIdentifier does not exist diff --git a/src/lib/services/transaction-service.ts b/src/lib/services/transaction-service.ts index 4c4d4c3..ec7cae9 100644 --- a/src/lib/services/transaction-service.ts +++ b/src/lib/services/transaction-service.ts @@ -222,9 +222,7 @@ export async function submitSafeTransactionToService( ...transaction, }); } catch (err) { - throw new Error(err.response.data); - // do nothing? - return null; + throw new Error(err.response.data.nonFieldErrors); } return JSON.parse(result.config.data); } diff --git a/test/token.test.ts b/test/token.test.ts index 4e98150..40d59c4 100644 --- a/test/token.test.ts +++ b/test/token.test.ts @@ -40,7 +40,9 @@ beforeAll(async () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore provider.getSigner = () => { - return userAddress2; + return { + getAddress: jest.fn().mockResolvedValue(userAddress) + } }; sdk.init({ provider, network: 1 }); }); @@ -61,6 +63,17 @@ describe('admin actions', () => { }); test('As an admin, I should be able to mint a member to a pod', async () => { + provider = new ethers.providers.InfuraProvider('mainnet', { + infura: '69ecf3b10bc24c6a972972666fe950c8', + }); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + provider.getSigner = () => { + return { + getAddress: jest.fn().mockResolvedValue(orcanautPod.admin) + } + }; + mockGetPodFetchersByAddress(); const mockMint = jest.fn(); jest.spyOn(utils, 'getContract').mockReturnValueOnce({ @@ -210,22 +223,21 @@ describe('proposeMintMemberFromPod', () => { // This should be a member of admin pod. getAddress: jest.fn().mockResolvedValueOnce(orcanautPod.members[0]), }; - const createSafeTx = jest.spyOn(createSafe, 'createNestedProposal').mockReturnValueOnce({}); + const createSafeTx = jest.spyOn(createSafe, 'createSafeTransaction').mockReturnValueOnce({}); const adminPod = await sdk.getPod(orcanautAddress); // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); // Creates a proposal on the admin pod to mint a new member to subPod using admin privileges. - await subPod.proposeMintMemberFromPod(adminPod, userAddress2, mockSigner); + await subPod.mintMemberFromAdminPod(adminPod, userAddress2, mockSigner); expect(createSafeTx).toHaveBeenCalledWith( { - sender: adminPod.safe, - safe: subPod.safe, + sender: orcanautPod.members[0], + safe: adminPod.safe, to: memberTokenAddress, data: '0x94d008ef0000000000000000000000001cc62ce7cb56ed99513823064295761f9b7c856e0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000', }, - adminPod, mockSigner, ); }); @@ -233,8 +245,8 @@ describe('proposeMintMemberFromPod', () => { test('As a member of a subpod, I should be able to create a proposal on the parent pod to mint a member to the parent', async () => { setupAdminAndSubPod(); const mockSigner = { - // This should be a member of admin pod. - getAddress: jest.fn().mockResolvedValueOnce(orcanautPod.members[0]), + // This should be a member of sub pod. + getAddress: jest.fn().mockResolvedValueOnce(artNautPod.members[0]), }; const createSafeTx = jest.spyOn(createSafe, 'createNestedProposal').mockReturnValueOnce({}); @@ -243,7 +255,7 @@ describe('proposeMintMemberFromPod', () => { const subPod = await sdk.getPod('art-naut.pod.xyz'); // Creates a proposal on the admin pod to mint a new member to subPod using admin privileges. - await parentPod.proposeMintMemberFromPod(subPod, userAddress2, mockSigner); + await parentPod.mintMemberFromSubPod(subPod, userAddress2, mockSigner); expect(createSafeTx).toHaveBeenCalledWith( { sender: subPod.safe, @@ -256,38 +268,6 @@ describe('proposeMintMemberFromPod', () => { ); }); - test("Should throw if the adminPod is neither the admin, nor a subpod of the Pod you're trying to mint to", async () => { - jest.spyOn(fetchers, 'getPodFetchersByAddressOrEns').mockResolvedValueOnce({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Controller: { podAdmin: jest.fn().mockResolvedValue(orcaCorePod.admin) }, - safe: orcaCorePod.safe, - podId: orcaCorePod.id, - Name: { name: orcaCorePod.ensName }, - }).mockResolvedValueOnce({ - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - Controller: { podAdmin: jest.fn().mockResolvedValue(artNautPod.admin) }, - safe: artNautPod.safe, - podId: artNautPod.id, - Name: { name: artNautPod.ensName }, - }); - jest.spyOn(utils, 'getContract').mockReturnValueOnce({ - address: memberTokenAddress, - }); - - const mockSigner = { - // This is a member of orca core. - getAddress: jest.fn().mockResolvedValueOnce(userAddress2), - }; - - const adminPod = await sdk.getPod(orcanautAddress); - // subPod is member of adminPod - const subPod = await sdk.getPod('art-naut.pod.xyz'); - - await expect(subPod.proposeMintMemberFromPod(adminPod, userAddress2, mockSigner)).rejects.toThrow('must be the admin or a subpod of this pod to make proposals'); - }); - test('Should throw if the signer of proposeMintMemberFromPod is not a member of the external pod', async () => { setupAdminAndSubPod(); const mockSigner = { @@ -299,7 +279,7 @@ describe('proposeMintMemberFromPod', () => { // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); - await expect(subPod.proposeMintMemberFromPod(adminPod, userAddress2, mockSigner)).rejects.toThrow('was not a member of the external pod'); + await expect(subPod.mintMemberFromAdminPod(adminPod, userAddress2, mockSigner)).rejects.toThrow('was not a member of the admin pod'); }); test('Cannot mint a member that already exists on the sub pod', async () => { @@ -313,7 +293,7 @@ describe('proposeMintMemberFromPod', () => { // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); - await expect(subPod.proposeMintMemberFromPod(adminPod, artNautPod.members[0], mockSigner)).rejects.toThrow('is already in this pod'); + await expect(subPod.mintMemberFromAdminPod(adminPod, artNautPod.members[0], mockSigner)).rejects.toThrow('is already in this pod'); }); }); @@ -347,22 +327,21 @@ describe('proposeBurnMemberFromPod', () => { // This should be a member of admin pod. getAddress: jest.fn().mockResolvedValueOnce(orcanautPod.members[0]), }; - const createSafeTx = jest.spyOn(createSafe, 'createNestedProposal').mockReturnValueOnce({}); + const createSafeTx = jest.spyOn(createSafe, 'createSafeTransaction').mockReturnValueOnce({}); const adminPod = await sdk.getPod(orcanautAddress); // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); // Creates a proposal on the admin pod to mint a new member to subPod using admin privileges. - await subPod.proposeBurnMemberFromPod(adminPod, artNautPod.members[0], mockSigner); + await subPod.burnMemberFromAdminPod(adminPod, artNautPod.members[0], mockSigner); expect(createSafeTx).toHaveBeenCalledWith( { - sender: adminPod.safe, - safe: subPod.safe, + sender: orcanautPod.members[0], + safe: adminPod.safe, to: memberTokenAddress, data: '0x9dc29fac000000000000000000000000094a473985464098b59660b37162a284b51327530000000000000000000000000000000000000000000000000000000000000006', }, - adminPod, mockSigner, ); }); @@ -380,7 +359,7 @@ describe('proposeBurnMemberFromPod', () => { const subPod = await sdk.getPod('art-naut.pod.xyz'); // Creates a proposal on the admin pod to mint a new member to subPod using admin privileges. - await parentPod.proposeBurnMemberFromPod(subPod, orcanautPod.members[0], mockSigner); + await parentPod.burnMemberFromSubPod(subPod, orcanautPod.members[0], mockSigner); expect(createSafeTx).toHaveBeenCalledWith( { sender: subPod.safe, @@ -422,21 +401,21 @@ describe('proposeBurnMemberFromPod', () => { // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); - await expect(subPod.proposeBurnMemberFromPod(adminPod, artNautPod.members[0], mockSigner)).rejects.toThrow('must be the admin or a subpod of this pod to make proposals'); + await expect(subPod.burnMemberFromAdminPod(adminPod, artNautPod.members[0], mockSigner)).rejects.toThrow('must be the admin or a subpod of this pod to make proposals'); }); - test('Should throw if the signer of proposeMintMemberFromPod is not a member of the external pod', async () => { + test('Should throw if the signer of burnMemberFromSubPod is not a member of the sub pod', async () => { setupAdminAndSubPod(); const mockSigner = { // Not a member. - getAddress: jest.fn().mockResolvedValueOnce(userAddress2), + getAddress: jest.fn().mockResolvedValueOnce(userAddress), }; const adminPod = await sdk.getPod(orcanautAddress); // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); - await expect(subPod.proposeBurnMemberFromPod(adminPod, artNautPod.members[0], mockSigner)).rejects.toThrow('was not a member of the external pod'); + await expect(adminPod.burnMemberFromSubPod(subPod, artNautPod.members[0], mockSigner)).rejects.toThrow('was not a member of the sub pod'); }); test('Cannot mint a member that does not exist on the sub pod', async () => { @@ -450,11 +429,11 @@ describe('proposeBurnMemberFromPod', () => { // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); - await expect(subPod.proposeBurnMemberFromPod(adminPod, userAddress2, mockSigner)).rejects.toThrow('is not in this pod'); + await expect(subPod.burnMemberFromAdminPod(adminPod, userAddress2, mockSigner)).rejects.toThrow('is not in this pod'); }); }); -describe('proposeTransferMembershipFromSubPod', () => { +describe('transferMembershipFromSubPod', () => { function setupAdminAndSubPod() { jest.spyOn(fetchers, 'getPodFetchersByAddressOrEns').mockResolvedValueOnce({ // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -484,14 +463,14 @@ describe('proposeTransferMembershipFromSubPod', () => { // This should be a member of admin pod. getAddress: jest.fn().mockResolvedValueOnce(artNautPod.members[0]), }; - const createSafeTx = jest.spyOn(createSafe, 'createSafeTransaction').mockReturnValueOnce({}); + const createSafeTx = jest.spyOn(createSafe, 'createNestedProposal').mockReturnValueOnce({}); const adminPod = await sdk.getPod(orcanautAddress); // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); // Creates a proposal on the admin pod to mint a new member to subPod using admin privileges. - await adminPod.proposeTransferMembershipFromSubPod(subPod, userAddress2, mockSigner); + await adminPod.transferMembershipFromSubPod(subPod, userAddress2, mockSigner); expect(createSafeTx).toHaveBeenCalledWith( { sender: subPod.safe, @@ -499,6 +478,7 @@ describe('proposeTransferMembershipFromSubPod', () => { to: memberTokenAddress, data: '0xf242432a00000000000000000000000025f55d2e577a937433686a01439e5ffdffe622180000000000000000000000001cc62ce7cb56ed99513823064295761f9b7c856e0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000', }, + subPod, mockSigner, ); }); @@ -533,7 +513,7 @@ describe('proposeTransferMembershipFromSubPod', () => { const subPod = await sdk.getPod('art-naut.pod.xyz'); // Recipient address doesn't matter. - await expect(adminPod.proposeTransferMembershipFromSubPod(subPod, ethers.constants.AddressZero, mockSigner)).rejects.toThrow('must be a subpod of this pod to make proposals'); + await expect(adminPod.transferMembershipFromSubPod(subPod, ethers.constants.AddressZero, mockSigner)).rejects.toThrow('must be a subpod of this pod to make proposals'); }); test('Should throw if attempting to transfer membership to existing member', async () => { @@ -547,7 +527,7 @@ describe('proposeTransferMembershipFromSubPod', () => { // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); - await expect(adminPod.proposeTransferMembershipFromSubPod(subPod, artNautPod.members[0], mockSigner)).rejects.toThrow('is already in this pod'); + await expect(adminPod.transferMembershipFromSubPod(subPod, artNautPod.members[0], mockSigner)).rejects.toThrow('is already in this pod'); }); test('Should throw if the signer of proposeTransferMembershipFromSubPod is not a member of the external pod', async () => { @@ -561,7 +541,7 @@ describe('proposeTransferMembershipFromSubPod', () => { // subPod is member of adminPod const subPod = await sdk.getPod('art-naut.pod.xyz'); - await expect(adminPod.proposeTransferMembershipFromSubPod(subPod, userAddress2, mockSigner)).rejects.toThrow('was not a member of sub pod'); + await expect(adminPod.transferMembershipFromSubPod(subPod, userAddress2, mockSigner)).rejects.toThrow('was not a member of sub pod'); }); });