-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #23 from Once-Upon/benguyen0214/ou-1150-erc1155-pu…
…rchase Update NFT purchase/sale contextualizers
- Loading branch information
Showing
13 changed files
with
1,041 additions
and
243 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Transaction } from '../types'; | ||
import { detectERC1155Sale } from './erc1155Sale'; | ||
import erc1155Sale0x16b2334d from '../test/transactions/erc1155Sale-0x16b2334d.json'; | ||
|
||
describe('ERC1155 Sale', () => { | ||
it('Should detect ERC1155 Sale transaction', () => { | ||
const isERC1155Sale1 = detectERC1155Sale( | ||
erc1155Sale0x16b2334d as Transaction, | ||
); | ||
expect(isERC1155Sale1).toBe(true); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
import { ethers } from 'ethers'; | ||
import { Asset, Transaction } from '../types'; | ||
|
||
export function erc1155SaleContextualizer( | ||
transaction: Transaction, | ||
): Transaction { | ||
const isERC1155Sale = detectERC1155Sale(transaction); | ||
if (!isERC1155Sale) return transaction; | ||
|
||
return generateERC1155SaleContext(transaction); | ||
} | ||
|
||
/** | ||
* Detection criteria | ||
* | ||
* A tx is an ERC1155 sale when the tx.from sends and receives exactly 1 asset (look at netAssetTransfers). | ||
* The tx.from must send exactly 1 ERC1155, where the value (special to 1155s) can be arbitrary | ||
* The tx.from must receive either ETH/WETH/Blur ETH | ||
* There are no other recipients of ERC721/ERC20s/ERC1155s. | ||
*/ | ||
export function detectERC1155Sale(transaction: Transaction): boolean { | ||
/** | ||
* There is a degree of overlap between the 'detect' and 'generateContext' functions, | ||
* and while this might seem redundant, maintaining the 'detect' function aligns with | ||
* established patterns in our other modules. This consistency is beneficial, | ||
* and it also serves to decouple the logic, thereby simplifying the testing process | ||
*/ | ||
|
||
if (!transaction.netAssetTransfers) return false; | ||
|
||
const addresses = transaction.netAssetTransfers | ||
? Object.keys(transaction.netAssetTransfers) | ||
: []; | ||
// check if transfer.from sent and received one asset | ||
const transfers = transaction.netAssetTransfers[transaction.from]; | ||
const nftsSent = transfers.sent.filter((t) => t.type === 'erc1155'); | ||
const tokenReceived = transfers.received.filter( | ||
(t) => t.type === 'eth' || t.type === 'erc20', | ||
); | ||
|
||
if (nftsSent.length > 0 && tokenReceived.length > 0) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
function generateERC1155SaleContext(transaction: Transaction): Transaction { | ||
const receivingAddresses: string[] = []; | ||
const receivedNfts: Asset[] = []; | ||
const sentPayments: { type: string; asset: string; value: string }[] = []; | ||
|
||
for (const [address, data] of Object.entries(transaction.netAssetTransfers)) { | ||
const nftTransfers = data.received.filter((t) => t.type === 'erc1155'); | ||
const paymentTransfers = data.sent.filter( | ||
(t) => t.type === 'erc20' || t.type === 'eth', | ||
); | ||
if (nftTransfers.length > 0) { | ||
receivingAddresses.push(address); | ||
nftTransfers.forEach((nft) => receivedNfts.push(nft)); | ||
} | ||
if (paymentTransfers.length > 0) { | ||
paymentTransfers.forEach((payment) => | ||
sentPayments.push({ | ||
type: payment.type, | ||
asset: payment.asset, | ||
value: payment.value, | ||
}), | ||
); | ||
} | ||
} | ||
|
||
const receivedNftContracts = Array.from( | ||
new Set(receivedNfts.map((x) => x.asset)), | ||
); | ||
const totalPayments = Object.values( | ||
sentPayments.reduce((acc, next) => { | ||
acc[next.asset] = { | ||
type: next.type, | ||
asset: next.asset, | ||
value: ethers.BigNumber.from(acc[next.asset]?.value || '0') | ||
.add(next.value) | ||
.toString(), | ||
}; | ||
return acc; | ||
}, {}), | ||
) as { type: 'eth' | 'erc20'; asset: string; value: string }[]; | ||
|
||
transaction.context = { | ||
variables: { | ||
userOrUsers: { | ||
type: receivingAddresses.length > 1 ? 'emphasis' : 'address', | ||
value: | ||
receivingAddresses.length > 1 | ||
? `${receivingAddresses.length} Users` | ||
: receivingAddresses[0], | ||
}, | ||
tokenOrTokens: | ||
receivedNfts.length === 1 | ||
? { | ||
type: 'erc1155', | ||
token: receivedNfts[0].asset, | ||
tokenId: receivedNfts[0].tokenId, | ||
value: receivedNfts[0].value, | ||
} | ||
: receivedNftContracts.length === 1 | ||
? { | ||
type: 'address', | ||
value: receivedNftContracts[0], | ||
} | ||
: { | ||
type: 'emphasis', | ||
value: `${receivedNfts.length} NFTs`, | ||
}, | ||
price: | ||
totalPayments.length > 1 | ||
? { | ||
type: 'emphasis', | ||
value: `${totalPayments.length} Assets`, | ||
} | ||
: totalPayments[0].type === 'eth' | ||
? { | ||
type: 'eth', | ||
value: totalPayments[0].value, | ||
} | ||
: { | ||
type: 'erc20', | ||
token: totalPayments[0].asset, | ||
value: totalPayments[0].value, | ||
}, | ||
}, | ||
summaries: { | ||
category: 'NFT', | ||
en: { | ||
title: 'NFT Purchase', | ||
default: '[[userOrUsers]] [[bought]] [[tokenOrTokens]] for [[price]]', | ||
variables: { | ||
bought: { | ||
type: 'contextAction', | ||
value: 'bought', | ||
}, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
return transaction; | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Transaction } from '../types'; | ||
import { detectERC721Sale } from './erc721Sale'; | ||
import erc721Sale0x05b8cee6 from '../test/transactions/erc721Sale-0x05b8cee6.json'; | ||
|
||
describe('ERC721 Sale', () => { | ||
it('Should detect ERC721 Sale transaction', () => { | ||
const isERC721Sale1 = detectERC721Sale(erc721Sale0x05b8cee6 as Transaction); | ||
expect(isERC721Sale1).toBe(true); | ||
}); | ||
}); |
Oops, something went wrong.