Summary
@goodsdks/invite-sdk@1.0.1 prevents a user who first registered a personal invite code without an inviter from later attaching an inviter. InvitesV2 and GoodWallet support this one-time transition. Align InviteSDK prechecks with the contract, then release a corrected version for SDK-only consumers such as GoodWidget.
Scope
- Repo: GoodDollar/GoodSDKs
- Package:
@goodsdks/invite-sdk
- Main file:
packages/invite-sdk/src/sdks/viem-invite-sdk.ts — InviteSDK.join
- Reference contract: use gooddollar skills to look up the invite contract
- Tests: InviteSDK join/precheck tests
Current Problems
InviteSDK.join rejects any non-zero codeToUser(myCode), including when that code belongs to the connected caller.
- It also rejects any caller with
joinedAt > 0.
- InvitesV2 permits this valid sequence once:
join(myCode, zeroHash), then join(myCode, inviterCode) while invitedBy is zero and the bounty is unpaid.
- GoodWallet relies on that behavior by reusing the caller's existing personal code when an inviter code is supplied later.
Required Changes
- Permit
myCode when codeToUser(myCode) is the connected caller; continue rejecting codes owned by another address.
- Replace the blanket
joinedAt > 0 rejection with the InvitesV2 rule: allow one deferred inviter attachment only when the caller has no invitedBy, has not received their bounty, and supplies a valid non-zero inviter code.
- Continue rejecting a second join after an inviter is attached, after bounty payment, self-invites, inactive campaigns, and codes owned by another account.
- Release a new InviteSDK version and document this corrected
join behavior.
Implementation Notes
- Mirror
InvitesV2.join exactly: the existing caller-owned code is valid, and invitedBy == address(0) is the relevant deferred-assignment condition—not joinedAt == 0.
- Resolve the supplied inviter code before allowing the deferred path, so an unknown code remains rejected before transaction submission.
- Preserve typed, actionable errors for invalid inviter, self-invite, already-attached inviter, bounty-paid user, inactive contract, and conflicting code owner.
Acceptance Criteria
- A user can register with
join(myCode, zeroHash) and later successfully call join(myCode, inviterCode).
- The later call uses the same caller-owned
myCode and reaches SDK simulation/submission.
- A user cannot change or add another inviter once
invitedBy is non-zero.
- A code owned by another address, an unknown inviter code, self-invitation, inactive campaign, and a bounty-paid user remain rejected with typed errors.
- Existing initial join and collection behavior remains unchanged.
Testing / Verification
- Add an SDK test for initial self-registration followed by deferred inviter attachment.
- Add negative SDK tests for a second inviter attachment, a conflicting code owner, unknown inviter code, self-invite, and bounty-paid caller.
- Run the InviteSDK test suite and build.
- Verify a GoodWidget consumer can use the released SDK version without direct InvitesV2 writes.
Evidence
- InvitesV2 explicitly permits the deferred assignment in
contracts/invite/InvitesV2.sol:122-157 and tests it in test/invite/InvitesV2.test.ts:244-250.
- GoodWallet keeps invite-code entry available until
invitedBy is set and reuses the existing personal code.
- InviteSDK 1.0.1 currently rejects both caller-owned existing codes and callers with
joinedAt > 0 before simulation.
Summary
@goodsdks/invite-sdk@1.0.1prevents a user who first registered a personal invite code without an inviter from later attaching an inviter. InvitesV2 and GoodWallet support this one-time transition. Align InviteSDK prechecks with the contract, then release a corrected version for SDK-only consumers such as GoodWidget.Scope
@goodsdks/invite-sdkpackages/invite-sdk/src/sdks/viem-invite-sdk.ts—InviteSDK.joinCurrent Problems
InviteSDK.joinrejects any non-zerocodeToUser(myCode), including when that code belongs to the connected caller.joinedAt > 0.join(myCode, zeroHash), thenjoin(myCode, inviterCode)whileinvitedByis zero and the bounty is unpaid.Required Changes
myCodewhencodeToUser(myCode)is the connected caller; continue rejecting codes owned by another address.joinedAt > 0rejection with the InvitesV2 rule: allow one deferred inviter attachment only when the caller has noinvitedBy, has not received their bounty, and supplies a valid non-zero inviter code.joinbehavior.Implementation Notes
InvitesV2.joinexactly: the existing caller-owned code is valid, andinvitedBy == address(0)is the relevant deferred-assignment condition—notjoinedAt == 0.Acceptance Criteria
join(myCode, zeroHash)and later successfully calljoin(myCode, inviterCode).myCodeand reaches SDK simulation/submission.invitedByis non-zero.Testing / Verification
Evidence
contracts/invite/InvitesV2.sol:122-157and tests it intest/invite/InvitesV2.test.ts:244-250.invitedByis set and reuses the existing personal code.joinedAt > 0before simulation.