Skip to content

Commit dec52b9

Browse files
committed
feat: make GNS signal transferable
Signed-off-by: Tomás Migone <[email protected]>
1 parent 3da8723 commit dec52b9

File tree

3 files changed

+118
-1
lines changed

3 files changed

+118
-1
lines changed

contracts/discovery/GNS.sol

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
8686
uint256 tokensReceived
8787
);
8888

89+
/**
90+
* @dev Emitted when a curator transfers signal.
91+
*/
92+
event SignalTransferred(
93+
uint256 indexed subgraphID,
94+
address indexed from,
95+
address indexed to,
96+
uint256 nSignalTransferred
97+
);
98+
8999
/**
90100
* @dev Emitted when a subgraph is created.
91101
*/
@@ -455,6 +465,38 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
455465
emit SignalBurned(_subgraphID, curator, _nSignal, vSignal, tokens);
456466
}
457467

468+
/**
469+
* @dev Move subgraph signal from sender to `_recipient`
470+
* @param _subgraphID Subgraph ID
471+
* @param _recipient Address to send the signal to
472+
* @param _amount The amount of nSignal to transfer
473+
*/
474+
function transferSignal(
475+
uint256 _subgraphID,
476+
address _recipient,
477+
uint256 _amount
478+
) external override notPartialPaused returns (bool) {
479+
require(_recipient != address(0), "GNS: Curator cannot transfer to the zero address");
480+
481+
// Subgraph checks
482+
SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID);
483+
484+
// Balance checks
485+
address curator = msg.sender;
486+
uint256 curatorBalance = subgraphData.curatorNSignal[curator];
487+
require(curatorBalance >= _amount, "GNS: Curator transfer amount exceeds balance");
488+
489+
// Move the signal
490+
subgraphData.curatorNSignal[curator] = subgraphData.curatorNSignal[curator].sub(_amount);
491+
subgraphData.curatorNSignal[_recipient] = subgraphData.curatorNSignal[_recipient].add(
492+
_amount
493+
);
494+
495+
emit SignalTransferred(_subgraphID, curator, _recipient, _amount);
496+
497+
return true;
498+
}
499+
458500
/**
459501
* @dev Withdraw tokens from a deprecated subgraph.
460502
* When the subgraph is deprecated, any curator can call this function and

contracts/discovery/IGNS.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ interface IGNS {
6565
uint256 _tokensOutMin
6666
) external;
6767

68+
function transferSignal(
69+
uint256 _subgraphID,
70+
address _recipient,
71+
uint256 _amount
72+
) external returns (bool);
73+
6874
function withdraw(uint256 _subgraphID) external;
6975

7076
// -- Getters --

test/gns.test.ts

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const buildSubgraphID = (account: string, seqID: BigNumber): string =>
4848
describe('GNS', () => {
4949
let me: Account
5050
let other: Account
51+
let another: Account
5152
let governor: Account
5253

5354
let fixture: NetworkFixture
@@ -444,6 +445,34 @@ describe('GNS', () => {
444445
return tx
445446
}
446447

448+
const transferSignal = async (
449+
subgraphID: string,
450+
owner: Account,
451+
recipient: Account,
452+
amount: BigNumber,
453+
): Promise<ContractTransaction> => {
454+
// Before state
455+
const beforeOwnerNSignal = await gns.getCuratorSignal(subgraphID, owner.address)
456+
const beforeRecipientNSignal = await gns.getCuratorSignal(subgraphID, recipient.address)
457+
458+
// Transfer
459+
const tx = gns.connect(owner.signer).transferSignal(subgraphID, recipient.address, amount)
460+
461+
await expect(tx)
462+
.emit(gns, 'SignalTransfered')
463+
.withArgs(subgraphID, owner.address, recipient.address, amount)
464+
465+
// After state
466+
const afterOwnerNSignal = await gns.getCuratorSignal(subgraphID, owner.address)
467+
const afterRecipientNSignal = await gns.getCuratorSignal(subgraphID, recipient.address)
468+
469+
// Check state
470+
expect(afterOwnerNSignal).eq(beforeOwnerNSignal.sub(amount))
471+
expect(afterRecipientNSignal).eq(beforeRecipientNSignal.add(amount))
472+
473+
return tx
474+
}
475+
447476
const withdraw = async (account: Account, subgraphID: string): Promise<ContractTransaction> => {
448477
// Before state
449478
const beforeCuratorNSignal = await gns.getCuratorSignal(subgraphID, account.address)
@@ -475,7 +504,7 @@ describe('GNS', () => {
475504
}
476505

477506
before(async function () {
478-
;[me, other, governor] = await getAccounts()
507+
;[me, other, governor, another] = await getAccounts()
479508
fixture = new NetworkFixture()
480509
;({ grt, curation, gns } = await fixture.load(governor.signer))
481510
newSubgraph0 = buildSubgraph()
@@ -824,6 +853,46 @@ describe('GNS', () => {
824853
})
825854
})
826855

856+
describe('transferSignal()', async function () {
857+
let subgraph: Subgraph
858+
let otherNSignal: BigNumber
859+
860+
beforeEach(async () => {
861+
subgraph = await publishNewSubgraph(me, newSubgraph0)
862+
await mintSignal(other, subgraph.id, tokens10000)
863+
otherNSignal = await gns.getCuratorSignal(subgraph.id, other.address)
864+
})
865+
866+
it('should transfer signal from one curator to another', async function () {
867+
await transferSignal(subgraph.id, other, another, otherNSignal)
868+
})
869+
it('should fail when transfering to zero address', async function () {
870+
const tx = gns
871+
.connect(other.signer)
872+
.transferSignal(subgraph.id, ethers.constants.AddressZero, otherNSignal)
873+
await expect(tx).revertedWith('GNS: Curator cannot transfer to the zero address')
874+
})
875+
it('should fail when name signal is disabled', async function () {
876+
await deprecateSubgraph(me, subgraph.id)
877+
const tx = gns
878+
.connect(other.signer)
879+
.transferSignal(subgraph.id, another.address, otherNSignal)
880+
await expect(tx).revertedWith('GNS: Must be active')
881+
})
882+
it('should fail if you try to transfer on a non existing name', async function () {
883+
const subgraphID = randomHexBytes(32)
884+
const tx = gns
885+
.connect(other.signer)
886+
.transferSignal(subgraphID, another.address, otherNSignal)
887+
await expect(tx).revertedWith('GNS: Must be active')
888+
})
889+
it('should fail when the curator tries to transfer more signal than they have', async function () {
890+
const tx = gns
891+
.connect(other.signer)
892+
.transferSignal(subgraph.id, another.address, otherNSignal.add(otherNSignal))
893+
await expect(tx).revertedWith('GNS: Curator transfer amount exceeds balance')
894+
})
895+
})
827896
describe('withdraw()', async function () {
828897
let subgraph: Subgraph
829898

0 commit comments

Comments
 (0)