๐Ÿฆ„ ์œ ๋‹ˆ์Šค์™‘ V3๋ฅผ ํ†ตํ•ฉํ•œ ํ”„๋กœํ† ์ฝœ ๋งŒ๋“ค๊ธฐ

2022. 7. 11.

๋ชฉ์ฐจ

์†”๋ฆฌ๋””ํ‹ฐ๋กœ ์œ ๋‹ˆ์Šค์™‘์ด ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ A to Z ์•Œ์•„๋ณด๊ธฐ

์œ ๋‹ˆ์Šค์™‘์€ 2020๋…„ ๋ถ€ํ„ฐ 2021๋…„ ์ดˆ๊นŒ์ง€, DeFi Summer ๋™์•ˆ ์œ ๋™์„ฑ์„ ๋Œ์–ด์˜ฌ๋ฆฐ ์ค‘์ถ”์ ์ธ ์—ญํ• ์„ ๋‹ด๋‹นํ–ˆ์Šต๋‹ˆ๋‹ค. Automative Market Maker(์ดํ•˜ AMM)๋ผ๋Š” ๋…๋ณด์ ์ธ ๋„๊ตฌ๋กœ์จ, ์žฆ์€ ์•„๋น„ํŠธ๋ผ์ง€ ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•˜๋Š” ๋™์‹œ์—, DeFi ํ”„๋กœํ† ์ฝœ์— ์‰ฝ๊ฒŒ ํ†ตํ•ฉ๋˜์–ด ๋” ๋งŽ์€ ๊ฑฐ๋ž˜ ๋ณผ๋ฅจ๊ณผ ์œ ๋™์„ฑ์„ ์ฐฝ์ถœํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์ดํ›„์— ์œ ๋‹ˆ์Šค์™‘์„ ํฌํฌํ•œ ํ”„๋กœํ† ์ฝœ๊ณผ ์ž์‹ ๋งŒ์˜ ๋…ํŠนํ•œ ์ˆ˜์‹์„ ๊ฐ€์ง„ Balancer์™€ Curve, ๊ทธ๋ฆฌ๊ณ  ์ด ํ”„๋กœํ† ์ฝœ๋“ค์˜ ํฌํฌ๊นŒ์ง€ ๋งŽ์€ AMM๋“ค์ด ๋“ฑ์žฅํ•˜๊ฒŒ ๋˜์—ˆ๊ณ , ๊ฐ์ž ๊ฑฐ๋ฒ„๋„Œ์Šค ํ† ํฐ์„ ๋ฟŒ๋ฆฌ๋ฉด์„œ ์œ ๋ก€์—†๋Š” ์œ ๋™์„ฑ ์ž”์น˜๊ฐ€ ํŽผ์ณ์กŒ๋‹ค๊ณ  ํ•˜์—ฌ๋„ ๊ณผ์–ธ์ด ์•„๋‹ ๊ฒ๋‹ˆ๋‹ค.

์ดํ›„์—, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋…ํŠนํ•œ ๊ธฐ๋Šฅ๋“ค์„ ๊ฐ€์ง„ ์ƒˆ๋กœ์šด ์„ธ๋Œ€์˜ ์œ ๋‹ˆ์Šค์™‘ V3๊ฐ€ ๋ฐœํ‘œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ด ์œ ๋™์„ฑ์„ ์ง€์ •ํ•œ ๊ฐ€๊ฒฉ๋Œ€์— ์ง‘์ค‘์‹œํ‚ฌ ์ˆ˜ ์žˆ๋Š” ์ด ๊ธฐ๋Šฅ์ด ๋Œ€ํ‘œ์ ์ธ๋ฐ, ๊ฐ™์€ ๊ฐ€์น˜๋ฅผ ๊ฐ€์ง„ ํ† ํฐ๋“ค ์‚ฌ์ด์˜ ๊ตํ™˜์œผ๋กœ ์œ ๋ช…ํ•œ Stable Swap์˜ Curve์™€ ์œ ๋‹ˆ์Šค์™‘์„ ๋น„๊ตํ•œ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ ๋‹ˆ์Šค์™‘ V3๋ฅผ ์ด์šฉํ•˜์—ฌ, `500,000,000 Dai`๋ฅผ USDC๋กœ ๊ตํ™˜ํ•  ๋•Œ ์ˆ˜์ˆ˜๋ฃŒ์™€ 0.1%์˜ ๋‚ฎ์€ ์Šฌ๋ฆฌํ”ผ์ง€ ์˜ํ–ฅ
Dai๋ฅผ USDC๋กœ ์œ ๋‹ˆ์Šค์™‘ V3์„ ์ด์šฉํ•ด์„œ ๊ตํ™˜ํ•˜๋Š” ๋ชจ์Šต
Curve finance๋ฅผ ์ด์šฉํ•˜์—ฌ, `500,000,000 Dai`๋ฅผ USDC๋กœ ๊ตํ™˜ํ•  ๋•Œ ์ˆ˜์ˆ˜๋ฃŒ์™€ ์ƒ๋Œ€์ ์œผ๋กœ ๋†’์€ ์Šฌ๋ฆฌํ”ผ์ง€ ์˜ํ–ฅ
Dai๋ฅผ USDC๋กœ Curve finance๋ฅผ ์ด์šฉํ•ด์„œ ๊ตํ™˜ํ•˜๋Š” ๋ชจ์Šต

๋‹จ์ˆœ ์ˆ˜์ˆ˜๋ฃŒ์˜ ์ฐจ์ด์ด๊ธฐ ์ด์ „์— ๊ฒฐ๊ณผ๋กœ ๋ณด์ด๋“ฏ, ๊ฐ™์€ ๊ฐ€์น˜์— ๋Œ€ํ•œ ๊ตํ™˜์— ์žˆ์–ด์„œ ๊ทน๋‹จ์ ์ธ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ์—, ๊ฐ™์€ ๊ฐ€์น˜์˜ ์ž์‚ฐ์„ ๊ตํ™˜ํ•˜๋Š” ๋ฐ ์žˆ์–ด ์ตœ์ ์˜ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์—ฌ์ค€๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ํŠนํžˆ๋‚˜, ์ด๋Ÿฌํ•œ ์œ ๋™์„ฑ๋“ค์ด ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ด ๋ชจ์ด๊ฒŒ ๋œ๋‹ค๋Š” ์ ์— ์ฃผ๋ชฉํ•  ๋งŒํ•ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์ž๋ณธ ํšจ์œจ์„ฑ(Capital Efficiency)์ด ๊ทน๋Œ€ํ™”๋˜๋Š” ํ”„๋กœํ† ์ฝœ์„ ํ†ตํ•ฉํ•˜๋Š” ๊ฒƒ์€ ๋งŽ์€ ์ž ์žฌ์„ฑ์„ ๊ฐ€์ง€๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ๊ธฐํšŒ๋“ค์„ ์ ‘๋ชฉํ•˜๋Š” ๊ฒƒ์„ ๋ณด๋ฉด, ์•„์ง ์ˆจ๊ฒจ์ง„ ์ž ์žฌ์„ฑ์ด ๋งŽ์ง€ ์•Š์€๊ฐ€ ํ•˜๋Š” ์ƒ๊ฐ์ด ๋“ค๊ฒŒ๋” ํ•ฉ๋‹ˆ๋‹ค. ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•œ ์˜ˆ์‹œ๋Š” ์–ด๋–ค ๊ฒƒ์ด ์žˆ์„๊นŒ์š”?

Active LP

์œ ๋‹ˆ์Šค์™‘์ด ์œ ๋™์„ฑ์„ ํŠน์ • ๊ฐ€๊ฒฉ๋Œ€์— ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉด์„œ, ๊ฑฐ๋ž˜๊ฐ€ ์ž์ฃผ ์ผ์–ด๋‚˜๋Š” ๊ฐ€๊ฒฉ๋Œ€์— ๋งž์ถฐ ์œ ๋™์„ฑ์„ ์ง€์†์ ์œผ๋กœ ์˜ฎ๊ฒจ์ฃผ๋Š” ํ”„๋กœํ† ์ฝœ๋“ค์ด ๋‚˜์˜ค๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ด ํ”„๋กœํ† ์ฝœ๋“ค์€ ๊ตํ™˜์œผ๋กœ ๋ฐœ์ƒ๋˜๋Š” ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ฃผ๋œ ๋ชฉํ‘œ๋กœ ํ•˜์—ฌ ๊ฐ€๊ฒฉ๋Œ€๊ฐ€ ๋ณ€ํ•  ๋•Œ๋งˆ๋‹ค, ๊ธฐ์กด ์œ ๋™์„ฑ์ด ์œ„์น˜ํ•œ ๊ฐ€๊ฒฉ๋Œ€๋ฅผ ๋Œ€์‹ ํ•˜์—ฌ ์˜ฎ๊ฒจ์ค๋‹ˆ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ETH:USDC ์œ ๋™์„ฑ ํ’€์— 100๋ช…์ด ๊ฐ๊ฐ ์œ ๋™์„ฑ ๊ณต๊ธ‰์„ ํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ํ•  ๋•Œ, ๊ฑฐ๋ž˜๋˜๋Š” ๊ฐ€๊ฒฉ๋Œ€๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์ด๋ฅผ 100๋ช… ๋ชจ๋‘ ๊ฐ๊ฐ ์œ ๋™์„ฑ์ด ์œ„์น˜ํ•œ ๊ฐ€๊ฒฉ๋Œ€๋ฅผ ์˜ฎ๊ฒจ์•ผ ํ•˜์ง€๋งŒ, ์ด๋Ÿฌํ•œ ํ”„๋กœํ† ์ฝœ์„ ์ด์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด ๋ช‡ ๋ช…๋ถ„์˜ ์œ ๋™์„ฑ์ด๋“ , ํ•œ ๋ฒˆ์— ๋Œ€์‹  ์˜ฎ๊ฒจ์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ€์Šค๋น„๋ฅผ ์†Œ๋ชจํ•˜๋Š” ๋ถ€๋ถ„์— ์žˆ์–ด์„œ๋„ ๋” ์ ๊ฒŒ ์‚ฌ์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๊ฒƒ์„ ํ™œ์„ฑํ™”๋œ ์œ ๋™์„ฑ ๊ณต๊ธ‰(Active LP)์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด๋Ÿฐ ์ž๋™ํ™”๋œ ํ”„๋กœํ† ์ฝœ์€ ์Œ“์ธ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ฒญ๊ตฌํ•˜์—ฌ ๋‹ค์‹œ ์œ ๋™์„ฑ์œผ๋กœ ๋”ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ๋„ ๊ฐ€์ง€๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋ณต๋ฆฌ์˜ ํšจ๊ณผ ๋˜ํ•œ ๋ˆ„๋ฆด ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ผ๋ฐ˜ ์ด์šฉ์ž๊ฐ€ ์œ ๋‹ˆ์Šค์™‘์— ์œ ๋™์„ฑ ๊ณต๊ธ‰ํ•˜๋ฉด, ๊ณต๊ธ‰์˜ ์ฆํ‘œ๋กœ NFT๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฆํ‘œ๊ฐ€ NFT๋กœ ์ œ๊ณต๋˜์—ˆ์„ ๋•Œ ๋‹จ์ ์€ ๊ธฐ์กด ํ”„๋กœํ† ์ฝœ๊ณผ์˜ ํ†ตํ•ฉ์— ์–ด๋ ค์›€์„ ๊ฒช๋Š”๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ํ† ํฐ์„ ๋‹ด๋ณด๋กœ ๋Œ€์ถœ์„ ํ•˜๋Š” ํ”„๋กœํ† ์ฝœ์€ NFT๋ฅผ ์˜ˆ์น˜๋ฐ›๊ธฐ ์œ„ํ•ด์„œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ถ”๊ฐ€ ๊ฐœ๋ฐœํ•ด์•ผํ•˜๊ณ , NFT๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ •๋ณด๋ฅผ ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•˜๊ธฐ์— ํ”„๋กœํ† ์ฝœ์˜ ํ™•์žฅ์€ ํ•„์ˆ˜์ ์ž…๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ์ ์„ ๊ฐœ์„ ์‹œํ‚จ ๊ฒƒ์ด Gelato Network๋ผ๋Š” ์ž๋™ํ™” ํ”„๋กœํ† ์ฝœ์ด ์ œ๊ณตํ•˜๋Š” G-UNI ํ† ํฐ์ด ์žˆ์Šต๋‹ˆ๋‹ค. Gelato๋ฅผ ํ†ตํ•ด ์œ ๋‹ˆ์Šค์™‘์— ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•˜๊ฒŒ ๋˜๋ฉด ๊ฐ€๊ฒฉ๋Œ€๋ฅผ ์ž๋™์œผ๋กœ ์˜ฎ๊ฒจ์ค„ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์ •๊ธฐ์ ์œผ๋กœ ์Œ“์ธ ์ˆ˜์ˆ˜๋ฃŒ ๋˜ํ•œ ๋‹ค์‹œ ์œ ๋™์„ฑ์œผ๋กœ ๊ณต๊ธ‰ํ•˜์—ฌ ์ค๋‹ˆ๋‹ค. ๋˜ํ•œ ์œ ๋™์„ฑ์— ๋Œ€ํ•œ ์ฆํ‘œ๊ฐ€ ์ผ๋ฐ˜์ ์ธ ERC20์œผ๋กœ ์ œ๊ณต๋˜๊ธฐ ๋•Œ๋ฌธ์— MakerDAO์™€ ๊ฐ™์€ ๊ธฐ์กด ํ”„๋กœํ† ์ฝœ์— ํ†ตํ•ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

bean the DAO ์˜ํšŒ

์ œ ๊ฐœ์ธ์ ์ธ Social DAO๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด์„œ ๋ช‡๋ช‡ ์žฅ์น˜๋“ค์„ ๋„์ž…ํ•  ํ•„์š”๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์‹ค ์„ธ๊ณ„์˜ ์ฃผ์‹์€ ์˜ค๋”๋ถ์„ ์ด์šฉํ•œ ๊ตํ™˜์ด ์ž์ฃผ ์ผ์–ด๋‚˜๋Š”๋ฐ ๋ฐ˜ํ•ด, ๋ธ”๋ก์ฒด์ธ์—์„œ๋Š” ๋งŽ์€ ํ† ํฐ๋“ค์ด AMM์„ ์ด์šฉํ•˜์—ฌ ๊ตํ™˜๋œ๋‹ค๋Š” ์ ์„ ์ฃผ๋ชฉํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํŠนํžˆ๋‚˜ ์œ ๋™์„ฑ์ด ์ ์€ ํ† ํฐ์„ ์œ„ํ•ด์„œ ์œ ๋™์„ฑ์„ ํ•œ ๊ณณ์— ๋ชจ์„ ํ•„์š”๊ฐ€ ์žˆ์—ˆ๊ณ , ๋ฐ”์ด๋ฐฑ์ด๋‚˜ ํ† ํฐ ๋ฐœํ–‰์„ ํ†ตํ•œ ํฌ์„์„ ํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฐฉ๋ฒ•์„ ๊ณ ๋ฏผํ•˜๋˜ ๋์—, DAO์˜ ํˆฌํ‘œ๊ถŒ์ด ์œ ๋‹ˆ์Šค์™‘ bean:ETH์— ๋Œ€ํ•œ ์œ ๋™์„ฑ์ด ๋˜๋„๋ก ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด์„œ DAO์˜ ๊ฐ€์น˜๋ฅผ ์ง€์†์ ์œผ๋กœ ์ถ”์ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๊ณ , ์‚ฌ์šฉ์ž๋Š” ๊ฑฐ๋ฒ„๋„Œ์Šค ํ† ํฐ์„ ์Šคํ…Œ์ดํ‚น ํ•˜์˜€์„ ๋•Œ ์ตœ๋Œ€ ๊ฐ€์น˜๋ฅผ ๋А๋‚„ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์—ฌ, ๊ฑฐ๋ฒ„๋„Œ์Šค ์ฐธ์—ฌ๋ฅผ ๋”์šฑ ๋…๋ คํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์œ ๋™์„ฑ์ด ํ•œ๊ณณ์— ๋ชจ์ด๊ฒŒ ๋˜์—ˆ์œผ๋ฏ€๋กœ, TWAP์„ ํ†ตํ•ด DAO์˜ ๊ฐ€์น˜ ์‚ฐ์ •์ด ๋”์šฑ ์‰ฝ๊ฒŒ ์ด๋ค„์งˆ ์ˆ˜ ์žˆ๋‹ค๊ณ  ๋ณด์—ฌ์ง‘๋‹ˆ๋‹ค. ๊ฑฐ๋ฒ„๋„Œ์Šค๊ฐ€ ๊ฐ€์ง„ ํ† ํฐ๊ณผ ์œ ๋™์„ฑ์€ ์‰ฝ๊ฒŒ ๊ณ„์‚ฐ๋˜์–ด ์ž๋™ํ™”๋œ ๊ณต์‹œ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•œ๋‹ค๋Š” ์ , ๋˜ํ•œ ํˆฌํ‘œ๊ถŒ์ด ์œ ๋™์„ฑ์œผ๋กœ ์ด๋ค„์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋‘ ๊ฐœ์˜ ํ† ํฐ์ด ์•„๋‹ˆ๋ผ, ํ•˜๋‚˜์˜ ํ† ํฐ. ์˜ˆ๋ฅผ ๋“ค์–ด ์ˆœ์ˆ˜ํ•˜๊ฒŒ ETH๋งŒ ์Šคํ…Œ์ดํ‚นํ•˜๋Š” ๊ฒƒ์œผ๋กœ๋„ ๊ฑฐ๋ฒ„๋„Œ์Šค ์ฐธ์—ฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜๋ฏ€๋กœ DAO์— ์žˆ์–ด์„œ ์ง€์†์ ์ธ ๋„์ „์ด ๋˜๋ฆฌ๋ผ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๋Š” UniswapModule ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โ€˜bean the DAO ์˜ํšŒโ€™๋ฅผ ๊ฐœ๋ฐœํ•˜๋Š” ๋„์ค‘ ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•  ๋•Œ ๊ต‰์žฅํžˆ ๋งŽ์€ ๋ฌธ์ œ์— ๋ด‰์ฐฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์œ ๋‹ˆ์Šค์™‘ V3๋ถ€ํ„ฐ ์ธํ„ฐํŽ˜์ด์Šค๋“ค์ด ๊ธฐ๋Šฅ์— ๋”ฐ๋ผ ๋ถ„ํ• ๋˜์–ด ๋‹ค์–‘ํ•ด์กŒ๊ณ , ๋ฐฑ์„œ์— ๊ธฐ๋ก๋œ ๋‚ด์šฉ์ด ์‹ค์ œ ๊ตฌํ˜„๋œ ์ฝ”๋“œ๋ฅผ ์™„์ „ํžˆ ์„ค๋ช…ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„ ๊ฐœ๋ฐœ์ž ๋ฌธ์„œ๋ฅผ ํ™•์ธํ•˜๊ณ , ๋•Œ๋กœ๋Š” ๋ฌธ์„œ๋กœ๋„ ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•„ ์‹ค์ œ๋กœ ์‚ฌ์šฉ๋˜๋Š” ์˜ˆ์‹œ๋ฅผ ์ฐพ๊ธฐ ์œ„ํ•ด ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์–ด๋–ป๊ฒŒ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๋Š”์ง€ ํ™•์ธํ•  ํ•„์š”๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ, ์ด ๊ธ€์„ ํ†ตํ•ด ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•  ๋•Œ ๋™์ผํ•œ ๋ฌธ์ œ๋ฅผ ๊ฒช์ง€ ์•Š๋„๋ก, ์œ ๋‹ˆ์Šค์™‘์„ ์ฝ”๋“œ ๋ ˆ๋ฒจ์—์„œ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ์•ˆ๋‚ดํ•˜๊ณ , ์œ ๋‹ˆ์Šค์™‘ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์‚ฌ์šฉ ์˜ˆ์‹œ๋กœ ์•Œ๋ ค๋“œ๋ฆฌ๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

์œ ๋‹ˆ์Šค์™‘V3์„ ํ˜ธ์ถœํ•˜์—ฌ, ์œ ๋™์„ฑ ํ’€ ๋งŒ๋“ค๊ธฐ

๊ฐ€์žฅ ๋จผ์ €, ์›ํ•˜๋Š” ํ† ํฐ ์Œ์œผ๋กœ ์œ ๋™์„ฑ ํ’€์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์œ ๋™์„ฑ ํ’€์€ ์œ ๋‹ˆ์Šค์™‘ ํŒฉํ† ๋ฆฌ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ†ตํ•ด์„œ ์ƒ์„ฑ๋˜๋ฉฐ, ์œ ๋‹ˆ์Šค์™‘์˜ ๋ชจ๋“  ๋ฐฐํฌ๋œ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋Š” Uniswap Contract Deployments์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์•„์ฃผ ๋‹คํ–‰์ธ ์ ์€, ๋Œ€๋ถ€๋ถ„์˜ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์ฒด์ธ์— ๊ด€๊ณ„์—†์ด ๋™์ผํ•œ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";

import "./StandardToken.sol";

contract UniswapV3Integration {
    address public constant UNIV3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
    IUniswapV3Factory v3Factory = IUniswapV3Factory(UNIV3_FACTORY);
    IUniswapV3Pool pool;
    StandardToken token0;
    StandardToken token1;

    uint24 constant fee = 3000;

    constructor() {
        token0 = new StandardToken("bean the token", "bean", 18, 100000e18);
        token1 = new StandardToken("Wrapped Ether", "WETH", 18, 0);
        pool = IUniswapV3Pool(v3Factory.createPool(address(token0), address(token1), fee));
    }
}

์ด ์ฝ”๋“œ๋Š” ์œ ๋‹ˆ์Šค์™‘์„ ์ง์ ‘์ ์œผ๋กœ ํ†ตํ•ฉํ•˜๋Š” ์ปจํŠธ๋ž™ํŠธ์ด๋ฉฐ, ์œ ๋‹ˆ์Šค์™‘์˜ ์ธํ„ฐํŽ˜์ด์Šค๋Š” npm ์„ ํ†ตํ•ด์„œ ์‰ฝ๊ฒŒ ์ถ”๊ฐ€ํ•ด์„œ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๋ฐฐํฌ๋  ๋•Œ, ๋‘ ๊ฐœ์˜ ํ‘œ์ค€ ERC20์„ ์ƒ์„ฑํ•˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ๊ฐ๊ฐ bean๊ณผ WETH๋ผ๋Š” ํ† ํฐ์œผ๋กœ ๋ฐฐํฌ๋˜๋ฉฐ, ์œ ๋™์„ฑ ํ’€์„ ๋งŒ๋“ค ๋•Œ ํ•„์š”ํ•œ ๋‘ ๊ฐœ์˜ ํ† ํฐ์œผ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด ๋‘ ํ† ํฐ์„ ์ด์šฉํ•˜์—ฌ ์œ ๋‹ˆ์Šค์™‘ ํŒฉํ† ๋ฆฌ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์œ ๋™์„ฑ ํ’€์„ ์ƒ์„ฑํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

ํŒฉํ† ๋ฆฌ ์ปจํŠธ๋ž™ํŠธ์˜ createPool ํ•จ์ˆ˜ ๋‚ด๋ถ€๋ฅผ ๋ณด๋ฉด ๋‘ ํ† ํฐ์˜ ์ฃผ์†Œ์™€ ์ˆ˜์ˆ˜๋ฃŒ ๊ฐ’์„ ์ž…๋ ฅ๋ฐ›๊ฒŒ ๋˜๋Š”๋ฐ, ์ด ์ •๋ณด๋“ค์€ ์ถฉ๋Œํ•˜์ง€ ์•Š๋Š” ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋ฅผ ๊ตฌ์„ฑํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

...
    new UniswapV3Pool{salt: keccak256(abi.encode(token0, token1, fee))}();
...

์†”๋ฆฌ๋””ํ‹ฐ์—์„œ๋Š” ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋ฐฐํฌํ•  ๋•Œ salt๋ฅผ ์ง€์ •ํ•˜๋ฉด create2๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐฐํฌ๋˜๋ฉฐ, salt ๊ฐ’์— ๋”ฐ๋ผ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋ฅผ ํ™•์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ๋™์„ฑ ํ’€์ด create2๋ฅผ ์ด์šฉํ•˜์—ฌ ๋ฐฐํฌ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ์‚ฌ์šฉ๋˜๋Š” salt๋Š” โ€˜token0์˜ ์ฃผ์†Œ, token1์˜ ์ฃผ์†Œ, ๊ทธ๋ฆฌ๊ณ  ์ˆ˜์ˆ˜๋ฃŒ ๊ฐ’โ€™์„ ์ˆœ์„œ๋Œ€๋กœ ๋ฐฐ์น˜ํ•˜์—ฌ keccak256๋กœ ํ•ด์‹ฑ ํ•œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด, token0๊ณผ token1์˜ ์ˆœ์„œ๋ฅผ ๋ฐ”๊พธ๋ฉด ์ „ํ˜€ ๋‹ค๋ฅธ ํ•ด์‹œ๊ฐ’์ด salt๋กœ ์‚ฌ์šฉ๋˜๋Š”๋ฐ, ๊ฐ™์€ ํ† ํฐ ๊ตฌ์„ฑ๊ณผ ์ˆ˜์ˆ˜๋ฃŒ ์ฒด๊ณ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ ์ƒˆ๋กœ์šด ์œ ๋™์„ฑ ํ’€์„ ๋งŒ๋“ค๊ฒŒ ๋˜์ง€ ์•Š์„๊นŒ์š”?

// ๊ฒฐ๊ณผ: 0xd240917b4f29206a2f1fa9ca8c04135d5fb29b9de9297735e850d15b6343ea43
function sampleHashOne() external pure returns(bytes32) {
    return keccak256(abi.encode(address(1), address(2), 3000));
}

// ๊ฒฐ๊ณผ: 0x55b5bb173ff5ab0df513148295f13e7d273d1e487d43c8e8221c3cf0816df006
function sampleHashTwo() external pure returns(bytes32) {
    return keccak256(abi.encode(address(2), address(1), 3000));
}

์—ฌ๊ธฐ์—์„œ ์œ ๋‹ˆ์Šค์™‘์˜ ํŠน์ง• ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋‚˜์˜ค๋Š”๋ฐ, โ€˜์œ ๋‹ˆ์Šค์™‘์€ ํ† ํฐ์˜ ์ˆœ์„œ๋ฅผ ๊ต‰์žฅํžˆ ์ค‘์š”ํ•˜๊ฒŒ ์ƒ๊ฐํ•˜๊ณ  ์žˆ๋‹ค.โ€™ ๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์œ ๋‹ˆ์Šค์™‘ ์ „๋ฐ˜์ ์œผ๋กœ ๋‘ ๊ฐœ์˜ ํ† ํฐ ์ฃผ์†Œ๊ฐ€ ๋“ค์–ด์™”์„ ๋•Œ, token0์—๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ์ž‘์€ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง„ ํ† ํฐ์ด, token0์—๋Š” ์ƒ๋Œ€์ ์œผ๋กœ ํฐ ์ฃผ์†Œ๋ฅผ ๊ฐ€์ง„ ํ† ํฐ์ด ์˜ค๋„๋ก ์กฐ์ •๋ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ์ด ๊ฐ€๋Šฅํ•œ ์ด์œ ๋Š”, ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ ๋˜ํ•œ 16์ง„์ˆ˜๋กœ ๊ตฌ์„ฑ๋œ ์ˆซ์ž์ด๊ธฐ ๋•Œ๋ฌธ์— ๋น„๊ต๊ฐ€ ๊ฐ€๋Šฅํ•˜๊ณ , ์ฃผ์†Œ ์ž์ฒด๊ฐ€ ํ•ด์‹œ๋ฅผ ํ†ตํ•ด ๊ฒฐ์ •๋œ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ถฉ๋Œ์ด ์žˆ์„ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํŠน์„ฑ์œผ๋กœ ํฌ๊ณ  ์ž‘์Œ์— ๋Œ€ํ•œ ๋น„๊ต๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ํ† ํฐ ์ˆœ์„œ์˜ ์ค‘์š”์„ฑ์€ ์œ ๋™์„ฑ ํ’€์ด ์ƒ์„ฑ๋  ๋•Œ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ, ์•ž์œผ๋กœ ๋‚˜์˜ฌ ๋‹ค๋ฅธ ๊ธฐ๋Šฅ๋“ค์—๋„ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.

์ง‘์ค‘ํ™”๋œ ์œ ๋™์„ฑ ๊ณต๊ธ‰ ์ˆ˜ํ–‰ํ•˜๊ธฐ

์ƒˆ๋กœ์šด ์œ ๋‹ˆ์Šค์™‘์„ ์ด์šฉํ•˜์—ฌ ์›ํ•˜๋Š” ๊ฐ€๊ฒฉ๋Œ€์— ์œ ๋™์„ฑ์„ ์ง‘์ค‘์‹œํ‚ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉด์„œ ์œ ๋‹ˆ์Šค์™‘์—์„œ์˜ ๊ตํ™˜๋น„์œจ์ด ์ข‹์€ ๊ฒฝ์šฐ๊ฐ€ ์ข…์ข… ๋ฐœ๊ฒฌ๋˜๊ณค ํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ๋‚˜, ๊ฐ€์น˜๊ฐ€ ๊ฐ™์€ ํ† ํฐ๋“ค ์‚ฌ์ด์˜ ์œ ๋™์„ฑ ํ’€์—์„œ ์ด์ ์ด ๊ทน๋Œ€ํ™”๋˜์—ˆ๋Š”๋ฐ DAI/USDC์˜ ๊ฒฝ์šฐ๋ฅผ ์˜ˆ์‹œ๋กœ ๋ณผ ์ˆ˜ ์žˆ๊ฒ ์Šต๋‹ˆ๋‹ค.

์œ ๋‹ˆ์Šค์™‘ V3 Dai/USDC Pool, ๊ฑฐ์˜ 1:1์— ๊ฐ€๊นŒ์šด ๋น„์œจ๋กœ๋งŒ ์œ ๋™์„ฑ์ด ์ง‘์ค‘๋œ ๋ชจ์Šต
Dai์™€ USDC ์œ ๋‹ˆ์Šค์™‘ ํ’€์˜ ์œ ๋™์„ฑ ๋ฒ”์œ„

๋‘ ํ† ํฐ์˜ ๊ฐ€์น˜๊ฐ€ ๊ฑฐ์˜ ๋™์ผํ•˜๊ฒŒ ํ‰๊ฐ€๋˜๋‹ค ๋ณด๋‹ˆ, 0.9998~1.0001 ๊ฐ€๊ฒฉ ์‚ฌ์ด์— ์œ ๋™์„ฑ์ด ๊ณต๊ธ‰๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์œ ๋™์„ฑ์ด ๋„“์€ ๋ฒ”์œ„๋ฅผ ์ปค๋ฒ„ํ•˜์ง€ ์•Š์•„ ํ† ํฐ์„ ๊ตํ™˜ํ•  ๋•Œ ๊ฐ€๊ฒฉ ๋ณ€๋™์„ฑ ๋˜ํ•œ ๊ทน๋„๋กœ ๋‚ฎ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ๋™์„ฑ ๊ณต๊ธ‰์ž๋“ค๋„, ๋‘ ๊ฐœ์˜ ํ† ํฐ์ด ๋™์ผํ•œ ๊ฐ€์น˜์— ์ˆ˜๋ ดํ•œ๋‹ค๋Š” ์ปจ์„ผ์„œ์Šค๊ฐ€ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ ๋‹ˆ์Šค์™‘์˜ ๋ฐœํ‘œ๋Œ€๋กœ ์ž๋ณธ ํšจ์œจ์„ฑ(Capital Efficiency)์ด ์—„์ฒญ๋‚˜๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด์ฒ˜๋Ÿผ ์ €ํฌ์˜ ์˜ˆ์ œ์—์„œ๋„ ์œ ๋™์„ฑ์„ ํŠน์ • ๊ฐ€๊ฒฉ ๋ฒ”์œ„์— ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ์•ž์„œ ๋งŒ๋“  ์œ ๋™์„ฑ ํ’€์— ํ† ํฐ์„ ๊ณต๊ธ‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ดˆ๊ธฐ ๊ฐ€๊ฒฉ์„ ์ง€์ •ํ•œ ๋‹ค์Œ, ๋“ฑ๋กํ•  ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์œ ๋‹ˆ์Šค์™‘ V2์—์„œ๋Š” ์œ ๋™์„ฑ ํ’€์ด ์ƒ์„ฑ๋˜๋ฉด ๊ฐ๊ฐ์˜ ํ† ํฐ ์ˆ˜๋Ÿ‰์„ ์˜ˆ์น˜ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ดˆ๊ธฐ ๊ฐ€๊ฒฉ๊ณผ ๊ตํ™˜ ๋น„์œจ์„ ์„ค์ •ํ•˜์˜€๋‹ค๋ฉด, V3์—์„œ๋Š” ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•˜์—ฌ ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•  ์ˆ˜๋ฐ–์— ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‹จ์ˆœํžˆ ํ† ํฐ์˜ ๋น„์œจ๋กœ ์ดˆ๊ธฐ ๊ฐ€๊ฒฉ๊ณผ ๊ตํ™˜๋น„์œจ์„ ์•Œ ์ˆ˜ ์—†๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ์—, โ€˜์ดˆ๊ธฐ ๊ฐ€๊ฒฉโ€™๊ณผ โ€˜ํ† ํฐ์„ ๋“ฑ๋กํ•  ๊ฐ€๊ฒฉ ๋ฒ”์œ„โ€™๋ฅผ ์œ ๋™์„ฑ ํ’€์— ๊ธฐ๋กํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐ ์ง€์ถ•์˜ ์–‘ ๋์—๋Š” ๋ฌด์ œํ•œ์— ํ•ด๋‹นํ•˜๋Š” ๊ฐ€๊ฒฉ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฉฐ, ๊ฐ€๊ฒฉ์˜ ๋ถ„ํฌ์— ๋”ฐ๋ผ ์œ ๋™์„ฑ์ด ์ง‘์ค‘๋œ ๋ชจ์Šต
์œ ๋‹ˆ์Šค์™‘์˜ ์œ ๋™์„ฑ ๋ถ„ํฌ ์˜ˆ์‹œ

์œ ๋‹ˆ์Šค์™‘์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฐ€๊ฒฉ์€ ์ œ๊ณฑ๊ทผ(Squared Root)์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. โ€˜์ดˆ๊ธฐ ๊ฐ€๊ฒฉโ€™๊ณผ โ€˜ํ† ํฐ์„ ๋“ฑ๋กํ•  ๊ฐ€๊ฒฉ ๋ฒ”์œ„โ€™์„ ์ œ๊ณฑ๊ทผ ํ˜•ํƒœ๋กœ ๊ณ„์‚ฐํ•˜์—ฌ ์œ ๋™์„ฑ ํ’€์— ๋“ฑ๋กํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฐฑ์„œ์— ์ ํžŒ ์ˆ˜์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

P=yx\sqrt{P} = \sqrt{\frac{y}{x}}

์ด๊ฒƒ์€ ์œ ๋‹ˆ์Šค์™‘์ด ๋”ฐ๋ฅด๊ณ  ์žˆ๋Š” ๊ธฐ๋ณธ ์ˆ˜์‹์ธ xโˆ—y=Kx*y=K์— ํ•ด๋‹นํ•˜๋Š” ๊ฒƒ์ด๋ผ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ์ด ์ˆ˜์‹์„ ํ†ตํ•ด์„œ 1 ETH๊ฐ€ 1,800 DAI๋ผ๋Š” ๊ฐ€๊ฒฉ์„ ๊ณ„์‚ฐํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

P=11800\sqrt{P} = \sqrt{\frac{1}{1800}}

๊ทธ๋Ÿฌ๋‚˜ ์•ž์„œ์„œ๋„ ๋งํ–ˆ๋“ฏ, ์œ ๋‹ˆ์Šค์™‘์—์„œ๋Š” ํ† ํฐ์˜ ์ˆœ์„œ๊ฐ€ ๋ฌด์ฒ™ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์–ด๋–ค ํ† ํฐ์ด ๋ถ„์ž์™€ ๋ถ„๋ชจ์— ์œ„์น˜ํ•ด์•ผ ํ•˜๋Š”์ง€ ์•Œ์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. WETH์˜ ํ† ํฐ ์ฃผ์†Œ๋Š” 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2์ด๊ณ , DAI์˜ ํ† ํฐ ์ฃผ์†Œ๋Š” 0x6b175474e89094c44da98b954eedeac495271d0f์ž…๋‹ˆ๋‹ค. ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ์˜ ํฌ๊ธฐ ๋น„๊ต๋ฅผ ํ•˜๋ฉด, ์œ ๋™์„ฑ ํ’€์˜ token0์€ DAI์ด๋ฉฐ, token1์€ WETH๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋ฅผ ์ด์šฉํ•˜์—ฌ sqrtPrice๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ์†”๋ฆฌ๋””ํ‹ฐ ์ฝ”๋“œ๋ฅผ ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

uint160 sqrtPriceX96;
uint256 PRECISION = 2**96;
// 1 weth๊ฐ€ 1800 DAI์ธ ๊ฒฝ์šฐ. weth๋Š” ์†Œ์ˆ˜์  18์ž๋ฆฌ ์ง€์›, DAI ๋˜ํ•œ ์†Œ์ˆ˜์  18์ž๋ฆฌ ์ง€์›.
sqrtPriceX96 = uint160(sqrt((1e18 * PRECISION * PRECISION) / 1800e18));
// ๊ฒฐ๊ณผ: 1867425699159537997291064607

sqrtPriceX96 = uint160(sqrt((1800e18 * PRECISION * PRECISION) / 1e18));
// ๊ฒฐ๊ณผ: 3361366258487168395123916293647

์•„๋ž˜์˜ ๊ฒฐ๊ณผ๊ฐ€ ํ† ํฐ์˜ ์ˆœ์„œ๋ฅผ ๋ฐ˜๋Œ€๋กœ ๋งŒ๋“  ๊ฒฐ๊ณผ์ธ๋ฐ, ์ „ํ˜€ ๋‹ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ค๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‹น์—ฐํ•œ ๊ฒฐ๊ณผ์ด๊ฒ ์ฃ ! ์ด๋ ‡๊ฒŒ ์œ ๋‹ˆ์Šค์™‘์—์„œ ์‚ฌ์šฉ๋˜๋Š” sqrtPrice์˜ ์ตœ๋Œ€ ์ตœ์†Ÿ๊ฐ’์€ ๊ฐ๊ฐ 1461446703485210103287273052203988822378723970342๊ณผ 4295128739๋กœ ์ •ํ•ด์ ธ ์žˆ๊ณ , ๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ ๋งค์นญ์„ ํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ, ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์„ ๋น„๊ตํ–ˆ์„๋•Œ ์ผ์น˜ํ•˜๋Š” ๋ชจ์Šต
์œ ๋‹ˆ์Šค์™‘์˜ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์˜ ๋น„๊ต

๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ์— ๋Œ€ํ•ด ์–ด๋–ป๊ฒŒ ์ผ์น˜๋˜๋Š”์ง€ ์•„์‹œ๊ฒ ์ง€์š”? ๋งŒ์•ฝ ํ† ํฐ ์ˆœ์„œ๋ฅผ ๋ฐ˜๋Œ€๋กœ ๊ตฌ์„ฑํ•˜์—ฌ ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ์„ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜, ํ† ํฐ ๊ตํ™˜, ์œ ๋™์„ฑ ๋“ฑ๋ก์„ ํ•˜๋ฉด ํŠธ๋žœ์žญ์…˜์ด ๋Œ€๋ถ€๋ถ„ ์‹คํŒจํ•˜๊ฒŒ ๋œ๋‹ค๋Š” ์ ์„ ์œ ์˜ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ, ์‚ฌ์šฉ์ž์˜ ๊ด€์ ์—์„œ๋Š” โ€œํ† ํฐ A๋Š” ํ† ํฐ B๋กœ ํ™˜์‚ฐํ–ˆ์„ ๋•Œ ์–ผ๋งˆ๊ฐ€ ๋˜์–ด์•ผ ํ•˜๋‚˜โ€ ๋˜๋Š”, โ€œ๊ฐ๊ฐ ์–ผ๋งˆ์—์„œ ํ† ํฐ ๊ณต๊ธ‰์„ ํ•  ๊ฒƒ์ธ์ง€โ€๋ฅผ ๊ณ ๋ คํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ํ† ํฐ์˜ ์ˆœ์„œ๋ฅผ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹ค์Œ์˜ ํ•จ์ˆ˜๋ฅผ ์ž‘์„ฑํ•˜์—ฌ ์ค๋‹ˆ๋‹ค.

/**
 * @notice ํ† ํฐ ์ฃผ์†Œ์˜ ์ˆœ์„œ์— ๋”ฐ๋ผ ์ œ๊ณฑ๊ทผ๋œ ๊ฐ€๊ฒฉ์„ ์ž๋™์œผ๋กœ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
 * @param addr0 ํ† ํฐ ์ฃผ์†Œ0
 * @param addr1 ํ† ํฐ ์ฃผ์†Œ1
 * @param reserve0 ํ† ํฐ ์ฃผ์†Œ0์— ํ•ด๋‹นํ•˜๋Š” ํ† ํฐ ์ˆ˜๋Ÿ‰
 * @param reserve1 ํ† ํฐ ์ฃผ์†Œ1์— ํ•ด๋‹นํ•˜๋Š” ํ† ํฐ ์ˆ˜๋Ÿ‰
 * @return sqrtPriceX96 ๊ฐ ๋ฆฌ์ €๋ธŒ์— ๋”ฐ๋ฅธ ํ˜„์žฌ ํ† ํฐ์˜ ๊ฐ€์น˜
 */
function encodeSqrtPrice(
    address addr0,
    address addr1,
    uint256 reserve0,
    uint256 reserve1
) public pure returns (uint160 sqrtPriceX96) {
    if (addr0 > addr1) {
        sqrtPriceX96 = uint160(sqrt((reserve0 * PRECISION * PRECISION) / reserve1));
    } else {
        sqrtPriceX96 = uint160(sqrt((reserve1 * PRECISION * PRECISION) / reserve0));
    }
}

์œ ๋‹ˆ์Šค์™‘์€ ์ œ๊ณฑ๊ทผ ๊ฐ’์œผ๋กœ ํ‘œ์‹œ๋˜๋Š” ๊ฐ€๊ฒฉ์„ ์‚ฌ์šฉํ•˜๋Š” ์ด์œ ๋กœ, โ€˜๊ธฐ๋ณธ AMM ์•Œ๊ณ ๋ฆฌ์ฆ˜์ธ xโˆ—y=Kx*y=K ์˜ ๊ธฐํ•˜ํ•™์  ํŠน์„ฑ์œผ๋กœ ์œ ๋‹ˆ์Šค์™‘์—์„œ ์‚ฌ์šฉ๋˜๋Š” ์ˆ˜ํ•™์— ์ž˜ ๋“ค์–ด ๋งž์Šต๋‹ˆ๋‹ค.โ€™๋ผ๊ณ  ์ด์•ผ๊ธฐํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ์•ž์„œ์„œ ๊ณ„์† ๋‚˜์™”๋˜ sqrtPrice์— ์ ‘๋ฏธ์–ด๋กœ ๋ถ™๋Š” X96์€ ๋ฌด์—‡์ผ๊นŒ์š”?

EVM์€ ๋ถ€๋™์†Œ์ˆ˜์ ์˜ ์˜ํ–ฅ์„ ๋ฐ›์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๋ชจ๋“  ์ •๋ณด๋ฅผ 256๋น„ํŠธ ๋‹จ์œ„๋กœ ์ €์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ€์žฅ ์‰ฌ์šด ์‹ค์ „ ์˜ˆ์‹œ๋กœ, ERC20 ์ž”๊ณ ๋Š” ์ •์ˆ˜๋กœ๋งŒ ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€๋งŒ, ์†Œ์ˆ˜์  ์ •๋ณด์— ๋”ฐ๋ผ 18์ž๋ฆฌ ์†Œ์ˆ˜์  ๋˜๋Š” 6์ž๋ฆฌ ์†Œ์ˆ˜์  ๋“ฑ์œผ๋กœ ํ‘œํ˜„๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž”๊ณ ๊ฐ€ 1๋กœ ์ €์žฅ๋˜์–ด ์žˆ๋‹ค๋ฉด, 18์ž๋ฆฌ ์†Œ์ˆ˜์ ์„ ์ง€์›ํ•˜๋Š” ํ† ํฐ์—์„œ๋Š” 0.000000000000000001๋กœ ํ‘œํ˜„๋˜๊ณ , 6์ž๋ฆฌ ์†Œ์ˆ˜์ ์„ ์ง€์›ํ•˜๋Š” ํ† ํฐ์—์„œ๋Š” 0.000001๋กœ ํ‘œํ˜„๋ฉ๋‹ˆ๋‹ค.

์œ ๋‹ˆ์Šค์™‘์—์„œ๋Š” ์ ‘๋ฏธ์–ด X๋’ค์— ๋”ฐ๋ผ์˜ค๋Š” ์ˆซ์ž๋ฅผ ์†Œ์ˆ˜์ ์„ ํ‘œํ˜„ํ•˜๋Š”๋ฐ ์˜ˆ์•ฝ๋œ ๋น„ํŠธ(bit)๋ผ๊ณ  ์„ค๋ช…ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. sqrtPriceX96 ์ž์ฒด๋Š” uint160์— ์ €์žฅํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, 160 - 96 = 64๋น„ํŠธ๋Š” ์ •์ˆ˜๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ข…์ข… ์œ ๋‹ˆ์Šค์™‘ ์ฝ”๋“œ ๋˜๋Š” ๋ฌธ์„œ๋ฅผ ๋ณด๋ฉด sqrtRatioX96 ์ด ๋‚˜ํƒ€๋‚˜๋Š”๋ฐ, sqrtPriceX96๊ณผ ๋™์ผํ•œ ์˜๋ฏธ๋กœ ์‚ฌ์šฉ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‹ค์‹œ ์ €ํฌ ์ฝ”๋“œ๋กœ ๋Œ์•„์˜ค๋ฉด, ์ด์ œ sqrtPriceX96์„ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ๊ณ„์‚ฐํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•  ๋•Œ ๋‘ ๊ฐœ์˜ ํ† ํฐ ๋ชจ๋‘๋ฅผ ์ค€๋น„ํ•˜๋Š” ๋ฐฉ๋ฒ•๋ณด๋‹ค ๋‘ ๊ฐœ์˜ ํ† ํฐ ์ค‘ ํ•˜๋‚˜๋งŒ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ์„ค์ •ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ, ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์„ ๋น„๊ตํ–ˆ์„๋•Œ ์ผ์น˜ํ•˜๋Š” ๋ชจ์Šต
๊ฐ€๊ฒฉ ๊ณต๊ฐ„์—์„œ, ํ˜„์žฌ์˜ ๊ฐ€๊ฒฉ ์ง€์ ๊ณผ, ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•˜๋ ค๋Š” ๊ฐ€๊ฒฉ์˜ ๋ฒ”์œ„

์šฐ์„  ์ €ํฌ๋Š” 1 bean์„ 0.01 ~ 10 ETH์˜ ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋กœ ๋“ฑ๋กํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ์— ์œ ๋™์„ฑ ํ’€์ด ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผ ํ•˜๋Š” ์ดˆ๊ธฐ ๊ฐ€๊ฒฉ์€ 0.01 bean/ETH ๊ฐ€ ์ ํ•ฉํ•˜๊ณ , ์ด๊ฒƒ์„ ๊ณ„์‚ฐํ•˜์—ฌ ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

uint160 initialPriceX96 = encodeSqrtPrice(address(token0), address(token1), 1e18, 0.01 ether);

// ์•ž์„œ ๋งŒ๋“  Pool์— ๊ฐ€๊ฒฉ ์ •๋ณด๋ฅผ ์ดˆ๊ธฐํ™” ํ•ฉ๋‹ˆ๋‹ค.
pool.initialize(initialPriceX96);

์ดˆ๊ธฐ ๊ฐ€๊ฒฉ ์ •๋ณด๋Š” ์œ ๋™์„ฑ์ด ๋“ฑ๋ก๋˜๊ธฐ ์ด์ „์—๋Š” ๋ช‡ ๋ฒˆ์ด๊ณ  ํ˜ธ์ถœ์ด ๊ฐ€๋Šฅํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ๋ฐ”๋กœ ์ดํ›„์— ์œ ๋™์„ฑ์ด ๊ณต๊ธ‰๋˜๋Š” ๊ฒƒ์ด ํ•ฉ๋‹นํ•ฉ๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์—ฌ๊ธฐ์—์„œ๋Š” ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ์ด ์ง์ ‘์ ์œผ๋กœ ์‚ฌ์šฉ๋˜์ง€ ์•Š๊ณ , ์ž๋ฃŒํ˜•์ด int24์ธ Tick์„ ์ด์šฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Tick์ด๋ž€ ์œ ๋™์„ฑ ํ’€์˜ ํ˜„์žฌ ๊ฐ€๊ฒฉ์ด 0.01% ๋ณ€๋™๋  ๋•Œ๋งˆ๋‹ค ํ•œ ์นธ์”ฉ ์›€์ง์ด๋Š” ๋ˆˆ๊ธˆ์ด๋ผ๊ณ  ์ƒ๊ฐํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ˆˆ๊ธˆ์˜ ๋‹จ์œ„๋Š” ์œ ๋™์„ฑ ํ’€์ด ๊ฐ€์ง€๋Š” ์ˆ˜์ˆ˜๋ฃŒ ์ฒด๊ณ„์™€๋„ ์ง์ ‘์ ์ธ ์ƒ๊ด€๊ด€๊ณ„๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

constructor() {
    ...
    feeAmountTickSpacing[500] = 10;
    emit FeeAmountEnabled(500, 10);
    feeAmountTickSpacing[3000] = 60;
    emit FeeAmountEnabled(3000, 60);
    feeAmountTickSpacing[10000] = 200;
    emit FeeAmountEnabled(10000, 200);
}

์ €ํฌ๊ฐ€ ์•ž์„œ ์ง€์ •ํ–ˆ๋˜ 0.3%์— ํ•ด๋‹นํ•˜๋Š” ์ˆ˜์ˆ˜๋ฃŒ์˜ 1 Tick์€ 60์ด๋ผ๋Š” ๋‹จ์œ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. 1% ์ˆ˜์ˆ˜๋ฃŒ์—์„œ๋Š” 200์ด๊ณ , 0.05% ์ˆ˜์ˆ˜๋ฃŒ์—์„œ๋Š” 10์ž…๋‹ˆ๋‹ค. ์ดํ›„์— ์ถ”๊ฐ€๋œ 0.01% ์ˆ˜์ˆ˜๋ฃŒ์—์„œ๋Š” 1์ž…๋‹ˆ๋‹ค.

๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ ํ‹ฑ ๊ณต๊ฐ„์„ ๋™์‹œ์— ๋‘๊ณ  ์ด๋ฅผ ๋น„๊ตํ•œ ์‚ฌ์ง„
๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ Tick ๊ณต๊ฐ„์ด ์ผ์น˜ํ•˜๋Š” ๋ชจ์Šต

๋‹คํ–‰์Šค๋Ÿฝ๊ฒŒ๋„ ์ด๋Ÿฌํ•œ Tick์„ ๋ณ€ํ™˜ํ•˜๊ณ  ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ œ๊ณต๋˜๋ฉฐ, ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ์„ Tick์œผ๋กœ ๋ณ€ํ™˜ํ•˜๊ฑฐ๋‚˜ ๊ทธ ๋ฐ˜๋Œ€์˜ ๋ณ€ํ™˜๋„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. Tick ์˜์—ญ ๋˜ํ•œ ์ตœ๋Œ€ ์ตœ์†Œ ๊ฐ’์ด ๊ฒฐ์ •๋˜์–ด ์žˆ๋Š”๋ฐ, integer๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์–ด 887272 ์—์„œ -887272๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ, ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ๊ณผ Tick์ด ์„œ๋กœ ๋ฐ”๋€” ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ ๋˜์—ˆ์œผ๋‹ˆ, ์›ํ•˜๋Š” ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ๋จผ์ € ๊ณ„์‚ฐํ•œ ๋‹ค์Œ์— ๊ทธ๊ฒƒ์„ Tick์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ฉด ์ €ํฌ๊ฐ€ ์›ํ•˜๋Š” ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ€๊ฒฉ์˜ ๋ฒ”์œ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์‹œ์ž‘ ๊ฐ€๊ฒฉ๊ณผ ๋ ๊ฐ€๊ฒฉ ์ด๋ ‡๊ฒŒ ๋‘ ๊ฐ€์ง€๋ฅผ ํ•„์š”๋กœ ํ•ฉ๋‹ˆ๋‹ค.

๋ ๊ฐ€๊ฒฉ์€ 10 bean/ETH์˜ ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ์„ ๊ตฌํ•œ ๋‹ค์Œ, Tick์œผ๋กœ ๋ณ€ํ™˜ ์‹œํ‚ค๋ฉด ๋˜์ง€๋งŒ, 0.01 bean/ETH ์— ๋Œ€ํ•œ ์ดˆ๊ธฐ ๊ฐ€๊ฒฉ์—์„œ 1 Tick ์ฆ๊ฐ€์‹œํ‚จ ๊ฐ’์„ ์‹œ์ž‘ ๊ฐ€๊ฒฉ์œผ๋กœ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋Š” ์ด์œ ๋Š” ์œ ๋™์„ฑ ๋ฒ”์œ„์—์„œ ํ•˜๋‚˜์˜ ํ† ํฐ๋งŒ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•˜๋ ค๋Š” ๋ชฉ์ ์ธ๋ฐ, ์ข€ ๋” ์ƒ์„ธํ•œ ์ด์œ ๋Š” ๋‚˜์ค‘์— ์„ค๋ช…๋ฉ๋‹ˆ๋‹ค.

import "@uniswap/v3-core/contracts/libraries/TickMath.sol";

int24 tickSpacing = 60; // 0.3% ์ˆ˜์ˆ˜๋ฃŒ ํ’€์˜ TickSpacing

uint160 upperPriceX96 = encodeSqrtPrice(address(token0), address(token1), 1e18, 10 ether);

int24 lowerTick = TickMath.getTickAtSqrtRatio(initialPriceX96) + tickSpacing;
int24 upperTick = TickMath.getTickAtSqrtRatio(upperPriceX96);

TickMath์˜ getTickAtSqrtRatio ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ์„ Tick์œผ๋กœ ๋ณ€ํ™˜์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ! ๋‹จ์ˆœํžˆ ์ด๋ ‡๊ฒŒ๋งŒ ์‚ฌ์šฉํ•˜๋ฉด ์œ ๋™์„ฑ์€ ๋“ฑ๋ก๋  ์ˆ˜๋„ ๋˜์ง€ ์•Š์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์„œ Tick์˜ ๋‹จ์œ„์— ๋Œ€ํ•ด ์•Œ๋ ค๋“œ๋ ธ๋Š”๋ฐ, ์ด๋ฅผ ๋‹ค๋ฅด๊ฒŒ์ด์•ผ๊ธฐ ํ•˜์ž๋ฉด ๋ชจ๋“  Tick ๊ฐ’์€ Tick์˜ ๋‹จ์œ„๋กœ ๋‚˜๋ˆ„์—ˆ์„ ๋•Œ ๋‚˜๋จธ์ง€ ๊ฐ’์ด ์—†์–ด์•ผ ํ•œ๋‹ค ๋Š” ์ ์ž…๋‹ˆ๋‹ค.

ํ‹ฑ ๊ณต๊ฐ„์—์„œ Tickํ•˜๋‚˜๋ฅผ ํ™•๋Œ€ํ•˜์—ฌ Tick์˜ ๋‹จ์œ„๋ฅผ ํ™•์ธํ•˜๋Š” ์‚ฌ์ง„
Tick ๊ณต๊ฐ„์—์„œ, ํ•˜๋‚˜์˜ ๊ฐ€๊ฒฉ ์ง€์ ์ด ๊ฐ€์ง€๊ณ  ์žˆ๋Š” Tick์˜ ๋‹จ์œ„

๊ตํ™˜๋  ํ† ํฐ์˜ 0.3%๊ฐ€ ์ˆ˜์ˆ˜๋ฃŒ๋กœ ์ ๋ฆฝ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์œ ๋™์„ฑ ๋ฒ”์œ„ ๋˜ํ•œ ์ˆ˜์ˆ˜๋ฃŒ์— ๋”ฐ๋ฅธ ๋‹จ์œ„, 1 Tick ๋‹จ์œ„๋กœ ์›€์ง์—ฌ์•ผ ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ณ„์‚ฐํ•œ Tick ๊ฐ’์„ Tick Spacing์œผ๋กœ ๋‚˜๋จธ์ง€๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š๋„๋ก ๊ณ„์‚ฐํ•˜์—ฌ, ๋‚˜๋จธ์ง€๊ฐ€ ์žˆ๋‹ค๋ฉด ๋นผ๊ณ  ๋ณด์ •ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค

if ((lowerTick % tickSpacing) != 0)
    lowerTick = lowerTick - (lowerTick % tickSpacing) + (lowerTick < 0 ? -tickSpacing : tickSpacing);
if ((upperTick % tickSpacing) != 0)
    upperTick = upperTick - (upperTick % tickSpacing) + (upperTick < 0 ? -tickSpacing : tickSpacing);

require(upperTick > lowerTick);

์•ž์„œ ๋ณธ Tick Space๋Š” ์Œ์ˆ˜์—์„œ ์–‘์ˆ˜๋กœ ์ด๋ค„์ ธ ์žˆ๊ณ , ๊ฐ€๊ฒฉ ๊ณต๊ฐ„๊ณผ 1:1 ์ผ์น˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋•Œ์—๋„ ํ† ํฐ์˜ ์ˆœ์„œ๊ฐ€ ๊ทธ๋Œ€๋กœ ์ ์šฉ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‚ฎ์€ Tick์€ ๋†’์€ Tick๋ณด๋‹ค ๋ฌด์กฐ๊ฑด ์ž‘์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด์ œ ์œ ๋™์„ฑ ๋ฒ”์œ„๋ฅผ ์œ„ํ•œ Tick์„ ๊ตฌํ–ˆ์œผ๋‹ˆ ์‹ค์งˆ์ ์œผ๋กœ ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

์œ ๋™์„ฑ์ด ๋“ฑ๋ก๋˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ˜„์žฌ ์œ ๋™์„ฑ ํ’€์˜ ๊ฐ€๊ฒฉ๊ณผ, ๊ฐ€๊ฒฉ ๋ฒ”์œ„, ๊ทธ๋ฆฌ๊ณ  ๋“ฑ๋กํ•˜๊ณ ์ž ํ•˜๋Š” ํ† ํฐ ์ˆ˜๋Ÿ‰์„ ๊ธฐ๋ฐ˜ํ•˜์—ฌ ๋‹จ์ผ ์ˆซ์ž๋กœ ๋‚˜ํƒ€๋‚ธ liquidity๋ฅผ ๊ณ„์‚ฐํ•ด ๋‚ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” LiquidityAmounts ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ getLiquidityForAmounts ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜์—์„œ๋Š” ํ˜„์žฌ ์œ ๋™์„ฑ ํ’€์˜ ์ œ๊ณฑ๊ทผ ๊ฐ€๊ฒฉ๊ณผ, ๊ฒ€์ฆ๋œ Tick ๊ฐ’์„ ๋‹ค์‹œ ์ œ๊ณฑ๊ทผ ๋œ ๊ฐ€๊ฒฉ์œผ๋กœ ๋ณ€๊ฒฝํ•˜์—ฌ ์ฃผ๊ณ , ๊ฐ๊ฐ์˜ ์œ ๋™์„ฑ ๊ณต๊ธ‰ํ•  ํ† ํฐ ์ˆ˜๋Ÿ‰์„ ์ ์–ด์ค๋‹ˆ๋‹ค.

import "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";

(uint256 token0Amount, uint256 token1Amount) = (0, 100000e18);

uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts(
    initialPriceX96,
    TickMath.getSqrtRatioAtTick(lowerTick),
    TickMath.getSqrtRatioAtTick(upperTick),
    token0Amount,
    token1Amount
);

์ €ํฌ๊ฐ€ ๋งŒ๋“ค์–ด๋‚ธ ํ† ํฐ bean๊ณผ WETH๋Š” ์ฃผ์†Œ๊ฐ€ ๊ฐ๊ฐ, 0x1866โ€ฆ ๊ณผ 0x0c7bโ€ฆ์ž…๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด ์œ ๋™์„ฑ ํ’€์˜ token0์€ WETH์ด๊ณ , token1์€ bean์ด ๋ฉ๋‹ˆ๋‹ค. ์ €ํฌ๋Š” bean ํ† ํฐ ํ•˜๋‚˜๋งŒ ๋“ฑ๋กํ•  ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, token1Amount์—๋งŒ ๋“ฑ๋กํ•  ์œ ๋™์„ฑ ์ˆ˜๋Ÿ‰์„ ์ ์–ด ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ์ด์ œ ์ •๋ง ๊ณ„์‚ฐํ•ด๋‚ธ liquidity๋ฅผ ์ด์šฉํ•˜์—ฌ ์œ ๋™์„ฑ ํ’€์— ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•  ์ฐจ๋ก€์ž…๋‹ˆ๋‹ค.

(uint256 amount0, uint256 amount1) = pool.mint(
    address(this),
    lowerTick,
    upperTick,
    liquidity,
    bytes("")
);

์ €ํฌ๊ฐ€ ๋งŒ๋“ค๊ณ  ์žˆ๋Š” ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์œ ๋™์„ฑ ํ’€์˜ mintํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ์ˆœ์„œ๋Œ€๋กœ ์œ ๋™์„ฑ์˜ ์†Œ์œ ์ž๋กœ ๊ธฐ๋ก๋  ์ฃผ์†Œ, ๋“ฑ๋กํ•  ๊ฐ€๊ฒฉ ์˜์—ญ์— ๋Œ€ํ•œ ๋‚ฎ์€ Tick๊ณผ ๋†’์€ Tick ๊ทธ๋ฆฌ๊ณ  ์•ž์„œ ๊ณ„์‚ฐํ•ด๋‚ธ liquidity ๊ฐ’์„ ๋„ฃ๊ณ , ๋งˆ์ง€๋ง‰์œผ๋กœ๋Š” ํ˜ธ์ถœ์ž์—๊ฒŒ ์ฝœ๋ฐฑ ํ•  ๋ฐ์ดํ„ฐ๋ฅผ ์ง‘์–ด๋„ฃ๋Š” ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ์ธ์ˆ˜๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์œ ๋‹ˆ์Šค์™‘ V3๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ์ปจํŠธ๋ž™ํŠธ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ ๋™์„ฑ ํ’€์€ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์•„๋‹Œ, ์ฝ”๋“œ๊ฐ€ ์—†๋Š” ์ง€๊ฐ‘๊ณผ ๋…๋‹จ์ ์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉํ•  ์ˆ˜ ์—†๋„๋ก ์„ค๊ณ„๊ฐ€ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

...
    uint256 balance0Before;
    uint256 balance1Before;
    if (amount0 > 0) balance0Before = balance0();
    if (amount1 > 0) balance1Before = balance1();
    IUniswapV3MintCallback(msg.sender).uniswapV3MintCallback(amount0, amount1, data);
    if (amount0 > 0) require(balance0Before.add(amount0) <= balance0(), 'M0');
    if (amount1 > 0) require(balance1Before.add(amount1) <= balance1(), 'M1');
...

์œ„ ์ฝ”๋“œ๋Š” ์œ ๋™์„ฑ ํ’€์˜ mint ํ•จ์ˆ˜ ๋‚ด๋ถ€์ž…๋‹ˆ๋‹ค. ์ค‘๊ฐ„์— ์ฝœ๋ฐฑ์„ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์—ฌ๊ธฐ์—์„œ msg.sender๋Š” ์œ ๋™์„ฑ ํ’€์„ ์ด์šฉํ•˜๋Š” ์ฃผ์†Œ๊ฐ€ ์žกํžˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์ธ EOA๋Š” ์ฝ”๋“œ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์œผ๋‹ˆ ์ € ๋ถ€๋ถ„์—์„œ ํŠธ๋žœ์žญ์…˜์ด ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์œ ๋‹ˆ์Šค์™‘ ์ด์šฉ์ž๋“ค์ด ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•  ๋•Œ์—๋Š” uniswapV3MintCallback ํ•จ์ˆ˜๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ๋Š” NonfungiblePositionManager์„ ์ด์šฉํ•˜๊ณ , NFT๋Š” ํ•ด๋‹น ์ปจํŠธ๋ž™ํŠธ์—์„œ ๋ฐœํ–‰๋˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋“ค์€, ์œ ๋™์„ฑ ํ’€์„ ํ˜ธ์ถœํ•œ ์ปจํŠธ๋ž™ํŠธ์—์„œ ์‹คํ–‰๋˜๋ฉฐ ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ํ† ํฐ์„ ์œ ๋™์„ฑ ํ’€๋กœ ์˜ฎ๊ธฐ๋Š” ์—ญํ• ์„ ํ•ฉ๋‹ˆ๋‹ค.

import "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol";

// Pool์—์„œ fallback์œผ๋กœ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜
function uniswapV3MintCallback(
    uint256 amount0Owed,
    uint256 amount1Owed,
    bytes calldata
) external {
    if (amount0Owed != 0) token0.transfer(address(pool), amount0Owed);
    if (amount1Owed != 0) token1.transfer(address(pool), amount1Owed);
}

ํ•ด๋‹น ํ•จ์ˆ˜๊ฐ€ ์‹คํ–‰๋˜๋Š” ์ปจํŠธ๋ž™ํŠธ๊ฐ€ bean ํ† ํฐ์„ ์†Œ์œ ํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์ผ๋ฐ˜์ ์ธ transfer ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ํ† ํฐ์ด ์œ ๋™์„ฑ ํ’€๋กœ ์˜ฎ๊ฒจ์ง€๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. ํ† ํฐ์„ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š๊ณ , ์‚ฌ์šฉ์ž์˜ ์ง€๊ฐ‘์—์„œ ํ† ํฐ์„ ์œ ๋™์„ฑ ํ’€๋กœ ์˜ฎ๊ฒจ์•ผ ํ•œ๋‹ค๋ฉด transferFrom ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜์—ฌ ์‚ฌ์šฉ์ž์˜ ์ง€๊ฐ‘์—์„œ ์œ ๋™์„ฑ ํ’€๋กœ ํ† ํฐ์ด ์ „์†ก๋˜๋„๋ก ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ฝœ๋ฐฑ์ด ๋๋‚˜์•ผ๋งŒ ์œ ๋™์„ฑ์ด ๋“ฑ๋ก๋˜๋Š”๋ฐ, ํ˜„์žฌ ์œ ๋™์„ฑ์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ๋‚˜๋จธ์ง€ ํ† ํฐ์ด ์ƒ๊ฒจ ์‹ค์ œ๋กœ ๋“ฑ๋ก๋˜๋Š” ํ† ํฐ์˜ ์ˆ˜๋Ÿ‰์ด 100000 ๊ฐœ๊ฐ€ ์•„๋‹Œ, 99999.999999999999999994์ธ ๊ฒƒ์ฒ˜๋Ÿผ ์•ฝ๊ฐ„์˜ ์†Œ์•ก์ด ๋‚จ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ธฐ๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์ฒด๋กœ ์ด๋Ÿฐ ๊ฒฝ์šฐ ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€๊ฒฉ ๋ฒ”์œ„๊ฐ€ ํ† ํฐ์„ ๋”ฑ ๋–จ์–ด์ง€๊ฒŒ ๋‹ด๊ธฐ์— ์ถฉ๋ถ„ํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, ๊ณ„์‚ฐ์ƒ์œผ๋กœ ๋‚˜๋จธ์ง€ ์ˆซ์ž๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ด๋Ÿฌํ•œ ์ ์ด ๋ฐœ์ƒํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ณ  ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. uniswapV3MintCallback ํ•จ์ˆ˜๊นŒ์ง€ ์ž˜ ํ˜ธ์ถœ๋˜์—ˆ๋‹ค๋ฉด, ์œ ๋™์„ฑ์€ ์ž˜ ๋“ฑ๋ก๋˜์—ˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์œ ๋‹ˆ์Šค์™‘์„ ์˜ค๋”๋ถ ์Šคํƒ€์ผ๋กœ ์ด์šฉํ•˜๊ธฐ

์œ ๋™์„ฑ์ด ํ’๋ถ€ํ•œ AMM์—์„œ ํŠน์ • ๊ฐ€๊ฒฉ๋Œ€์— ๊ตฌ๋งค๊ฐ€ ๊ฐ€๋Šฅํ•œ ์ฃผ๋ฌธ์„ ๋„ฃ๊ธฐ ์œ„ํ•ด์„œ๋Š” AMM ์™ธ๋ถ€์˜ ์š”์†Œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•˜์Šต๋‹ˆ๋‹ค. ํŠธ๋žœ์žญ์…˜์ด ๋ฏธ๋ž˜ ์‹œ์ ์— ์‹คํ–‰๋˜๊ธฐ ์œ„ํ•ด์„œ ํŠน์ • ์ฑ„๊ตด์ž๋ฅผ ๋ฏฟ์–ด์•ผ ํ–ˆ์œผ๋ฉฐ, ์ด๊ฒƒ์„ ์‹คํ–‰ํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ ์žˆ๋‹ค๋Š” ์ ์ด ๋ถ„๋ช…ํ•œ ๋‹จ์ ์œผ๋กœ ์กด์žฌํ–ˆ์Šต๋‹ˆ๋‹ค. V3๋ถ€ํ„ฐ๋Š” ์ง‘์ค‘ํ™”๋œ ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉด์„œ, ์™„๋ฒฝํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ์˜ค๋”๋ถ์ฒ˜๋Ÿผ ์ด์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

1์ด๋”๋‹น 1500๋‹ค์ด ์ผ๋•Œ ์œ ๋™์„ฑ ํ’€์— ๋“ฑ๋ก๋œ ์œ ๋™์„ฑ ์ง€ํ‘œ๋ฅผ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์—์„œ ํ™•์ธํ•œ ํ‘œ
1. ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์—์„œ, ํ˜„์žฌ ๊ฐ€๊ฒฉ๊ณผ ๊ฐ€๊ฒฉ๋Œ€์— ๋“ฑ๋ก๋œ ์œ ๋™์„ฑ

์ง์ ‘์ ์ธ ์˜ˆ์‹œ๋กœ์จ, DAI/ETH ์œ ๋™์„ฑ ํ’€์—์„œ 1 ETH๊ฐ€ 1,500 DAI์ธ ๊ฒฝ์šฐ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ๋ฅผ ์ƒ์ •ํ•˜์—ฌ ์ฃผ๋ฌธ์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ฒซ ๋ฒˆ์งธ๋กœ ETH์˜ ๊ฐ€๊ฒฉ ์ƒ์Šน์ด ์˜ˆ์ƒ๋˜์–ด ETH์„ 1,600Dai์— ํŒ๋งคํ•˜๋Š” ์ฃผ๋ฌธ์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‘ ๋ฒˆ์งธ๋Š” ETH๊ฐ€ ๋‚ฎ์•„์งˆ ๋•Œ ๊ตฌ๋งคํ•˜๊ธฐ ์œ„ํ•ด์„œ DAI๋ฅผ ์ด์šฉํ•˜์—ฌ ETH๋ฅผ ๊ตฌ๋งคํ•˜๋Š” ์ฃผ๋ฌธ์„ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์œ ๋™์„ฑ ์ง€ํ‘œ์—์„œ ํ˜„์žฌ ๊ฐ€๊ฒฉ์„ ํฌํ•จํ•˜์ง€ ์•Š๋Š” ๊ณณ์˜ ์œ ๋™์„ฑ ๋“ฑ๋ก
2. ํ˜„์žฌ ๊ฐ€๊ฒฉ์˜ ์˜ค๋ฅธ์ชฝ์—๋Š” โ€œ1,600 DAI์— ETH๋ฅผ ํŒ๋งคํ•˜๋Š” ์œ ๋™์„ฑ ์ฃผ๋ฌธโ€๊ณผ ์™ผ์ชฝ์˜ โ€œ900 DAI์— ETH๋ฅผ ๊ตฌ๋งคํ•˜๋Š” ์œ ๋™์„ฑ ์ฃผ๋ฌธโ€
๊ฐ€๊ฒฉ ๊ณต๊ฐ„์—์„œ ๋ฏธ๋ฆฌ ๋“ฑ๋กํ–ˆ๋˜ ์ฃผ๋ฌธ์ด ์ „๋ถ€ DAI๋กœ ๋ฐ”๋€ ๋ชจ์Šต
3. ์ดํ›„ ETH ๊ฐ€๊ฒฉ์ด ์ƒ์Šนํ•˜์—ฌ, ETH๋ฅผ 1,600 DAI์— ํŒ๋งคํ•˜๋Š” ์œ ๋™์„ฑ ์ „๋ถ€๊ฐ€ DAI๋กœ ๋ณ€ํ™˜๋จ
๊ฐ€๊ฒฉ ๊ณต๊ฐ„์—์„œ ๋ฏธ๋ฆฌ ๋“ฑ๋กํ–ˆ๋˜ ์ฃผ๋ฌธ์ด ์ „๋ถ€ ETH๋กœ ๋ฐ”๋€ ๋ชจ์Šต
4. ๋˜ ๋‹ค๋ฅธ ๊ฒฝ์šฐ๋กœ, ETH์˜ ๊ฐ€๊ฒฉ์ด ํ•˜๋ฝํ•˜์—ฌ 900 DAI์— ETH๋ฅผ ๊ตฌ๋งคํ•˜๋Š” ์œ ๋™์„ฑ์ด ์ „๋ถ€ ETH๋กœ ๋ณ€ํ™˜๋จ

๊ทธ๋Ÿฌ๋ฉด, ์ด๋Ÿฐ ํ˜•ํƒœ์˜ ์ฃผ๋ฌธ์€ ์–ด๋–จ๊นŒ์š”? ETH์˜ ๊ฐ€๊ฒฉ์ด 1,750 DAI์— ๋„๋‹ฌํ•˜๋Š” ๊ฒฝ์šฐ ์ดํ›„์— ์ถ”๊ฐ€์ ์ธ ์ƒ์Šน์„ ์˜ˆ์ƒํ•œ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜์—ฌ, 1 ETH๋ฅผ 1,750 DAI์— ๊ตฌ๋งคํ•˜๋Š” ์ฃผ๋ฌธ์„ ๋“ฑ๋กํ•˜๊ฑฐ๋‚˜, ETH์˜ ๊ฐ€๊ฒฉ์— ์ถ”๊ฐ€์ ์ธ ํ•˜๋ฝ์ด ์˜ˆ์ƒ๋˜๋Š” ๊ฒฝ์šฐ ํ•˜๋ฝ์„ ๊ฒช์ง€ ์•Š๊ธฐ ์œ„ํ•ด ETH๋ฅผ 1,400 DAI์— ํŒ๋งคํ•˜๋Š” ์ฃผ๋ฌธ์„ ๋“ฑ๋กํ•˜๋Š” ๊ฒ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์•„์‰ฝ๊ฒŒ๋„ ์ด๋Ÿฌํ•œ Stop ๊ณ„์—ด์˜ ์ฃผ๋ฌธ์€ ์œ ๋‹ˆ์Šค์™‘์— ๋“ฑ๋ก๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ Stop ๊ณ„์—ด ์ฃผ๋ฌธ๋“ค์„ AMM์˜ ์œ ๋™์„ฑ ๊ทธ๋ž˜ํ”„๋กœ ๋ณด์ž๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ฐ€๊ฒฉ์— ๋„๋‹ฌํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ๋ถˆ๊ฐ€๋Šฅํ•œ ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•œ ์˜ˆ์‹œ
๋ถˆ๊ฐ€๋Šฅํ•œ ์ฃผ๋ฌธ ํ˜•ํƒœ

์ด๋Ÿฐ ์˜ˆ์‹œ๋“ค์„ ๋ณด์•„์„œ ์•„์‹œ๊ฒ ์ง€๋งŒ, ์œ ๋™์„ฑ ๊ณต๊ธ‰๊ณผ ์˜ค๋”๋ถ ํ˜•ํƒœ์˜ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์ด ๋‹ค๋ฅด์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ๊ณผ ๊ทผ๋ณธ์ ์œผ๋กœ๋Š” ์œ ๋‹ˆ์Šค์™‘์˜ AMM ์‹œ์Šคํ…œ์„ ๋”ฐ๋ฅธ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ๊ฐ€๊ฒฉ์„ ๋ฒ—์–ด๋‚œ ๊ฐ€๊ฒฉ ์˜์—ญ์— ๋Œ€ํ•ด์„œ๋Š” ํ•˜๋‚˜์˜ ํ† ํฐ๋งŒ ๋“ฑ๋ก ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ , ํ˜„์žฌ์˜ ๊ฐ€๊ฒฉ์ด ์ง€์ •ํ•œ ๊ฐ€๊ฒฉ ์˜์—ญ์„ ์ง€๋‚˜๊ฐ€๋ฉด ์Œ์— ํ•ด๋‹นํ•˜๋Š” ํ† ํฐ๋งŒ ์œ ๋™์„ฑ์— ๋‚จ๋Š”๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ๊ฐ€์žฅ ์ข‹์€ ์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ์–ด๋–ค ๊ฒƒ์ผ๊นŒ์š”? ์ €ํฌ๊ฐ€ ์˜ค๋”๋ถ์— ์ฃผ๋ฌธ์„ ๋“ฑ๋กํ•  ๋•Œ์—๋Š” ๋”ฑ ๋–จ์–ด์ง€๋Š” ๊ฐ’์„ ์ž…๋ ฅํ•˜์ง€๋งŒ, ์œ ๋‹ˆ์Šค์™‘์—์„œ๋Š” ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ์ง€์ •ํ•ด์„œ ๋„ฃ์–ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ €ํฌ๋Š” ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฐ€์žฅ ์ตœ์†Œ ๋‹จ์œ„์ธ Tick์„ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

// ์ˆ˜์ˆ˜๋ฃŒ 0.3%์— ๋Œ€ํ•œ Tick ๋‹จ์œ„
int24 tickSpacing = 60;

// 1 ETH : 900 DAI์— ๋Œ€ํ•œ ๊ฐ€๊ฒฉ์„ ์‚ฐ์ถœํ•ฉ๋‹ˆ๋‹ค.
uint160 lowerPriceX96 = encodeSqrtPrice(address(weth), address(dai), 1 ether, 900e18);

// ๋‚ฎ์€ Tick๊ณผ ๋†’์€ Tick์„ ์ค€๋น„ํ•  ๋•Œ, ๋†’์€ Tick์€ ๋‚ฎ์€ Tick์—์„œ 1Tick ๋งŒํผ ์›€์ง์ธ ์˜์—ญ์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
int24 lowerTick = getTickAtSqrtRatio(lowerPriceX96);
int24 upperTick = getTickAtSqrtRatio(upperPriceX96) + tickSpacing;

// Tick ๊ฒ€์ฆ
if ((lowerTick % tickSpacing) != 0)
    lowerTick = lowerTick - (lowerTick % tickSpacing) + (lowerTick < 0 ? -tickSpacing : tickSpacing);
if ((upperTick % tickSpacing) != 0)
    upperTick = upperTick - (upperTick % tickSpacing) + (upperTick < 0 ? -tickSpacing : tickSpacing);

require(lowerTick < upperTick);

// token0 ์ด DAI์ด๊ธฐ ๋•Œ๋ฌธ์—, 9,000 Dai๋ฅผ ์ด์šฉํ•˜์—ฌ 10 ETH๋ฅผ ๊ตฌ๋งคํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
(uint256 token0Amount, uint256 token1Amount) = (9000e18, 0);

// ๋“ฑ๋ก๋  ์ฃผ๋ฌธ์— ๋Œ€ํ•œ ์œ ๋™์„ฑ์„ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.
uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts(
    sqrtPriceX96,
    TickMath.getSqrtRatioAtTick(lowerTick),
    TickMath.getSqrtRatioAtTick(upperTick),
    token0Amount,
    token1Amount
);

// ์ฃผ๋ฌธ์„ ๋“ฑ๋กํ•˜๊ณ , ์‹ค์ œ ๋“ฑ๋ก๋œ ์œ ๋™์„ฑ ์ˆ˜๋Ÿ‰์ด ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.
(uint256 amount0, uint256 amount1) = pool.mint(
    address(this),
    lowerTick,
    upperTick,
    liquidity,
    bytes("")
);

DAI/ETH ์œ ๋™์„ฑ ํ’€์ด ํ˜„์žฌ ๊ฐ€๊ฒฉ์œผ๋กœ 1500 DAI/ETH์— ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•˜๊ณ , 900 DAI/ETH๊ฐ€ ๋˜๋ฉด ์ž๋™์œผ๋กœ ๊ตฌ๋งค๊ฐ€ ๋˜๋„๋ก ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€๊ฒฉ ๋ฒ”์œ„๋Š” 900 DAI/ETH + 1 Tick ์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ์ดํ›„์— ์œ ๋™์„ฑ ํ’€์˜ ํ˜„์žฌ ๊ฐ€๊ฒฉ์ด 900 DAI/ETH ์ดํ•˜๋ผ๋ฉด, ํ•ด๋‹น ์œ ๋™์„ฑ์€ ETH๋กœ๋งŒ ๋‚จ์•„์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด ๋˜ํ•œ ์ง‘์ค‘ํ™”๋œ ์œ ๋™์„ฑ ๊ณต๊ธ‰ ๊ธฐ๋Šฅ์„ ์ด์šฉํ•˜์—ฌ AMM์— ์œ ๋™์„ฑ์„ ๋“ฑ๋กํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๊ธฐ ๋•Œ๋ฌธ์—, Pool์˜ ๊ธฐ์ค€ ๊ฐ€๊ฒฉ์ด 900 DAI/ETH ๊ฐ€ ๋„˜๊ธฐ ์ „์— ์œ ๋™์„ฑ์ด ์ œ๊ฑฐ๋˜์–ด์•ผ ์›ํ•˜๋Š” ํ† ํฐ์œผ๋กœ ์œ ๋™์„ฑ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ค๋ฉด ์œ ๋™์„ฑ ํ’€์— ์žˆ๋Š” ํ† ํฐ์€ ์–ด๋–ป๊ฒŒ ๊ตํ™˜ํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”?

SwapRouter๋ฅผ ์ด์šฉํ•˜์—ฌ ํ† ํฐ ๊ตํ™˜ํ•˜๊ธฐ

์œ ๋™์„ฑ ํ’€์€ ๋…์ž์ ์œผ๋กœ ์ž‘๋™ํ•  ์ˆ˜ ์—†๋‹ค ๋ณด๋‹ˆ, ์œ ๋‹ˆ์Šค์™‘์—์„œ ์ œ๊ณตํ•˜๋Š” SwapRouter๋ฅผ ์ด์šฉํ•˜์—ฌ ํ† ํฐ์„ ๊ตํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค. ๋˜ํ•œ ์‚ฌ์šฉ์ž์˜ ์ž…์žฅ์—์„œ ํ† ํฐ์˜ ํ—ˆ์šฉ์„ SwapRouter์—๋งŒ ํ•˜๋ฉด ๋˜๋‹ˆ, ํŽธ๋ฆฌํ•œ ์ ์ด ์žˆ๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค. ํ† ํฐ์„ ๊ตํ™˜ํ•˜๊ธฐ ์œ„ํ•œ SwapRouter์˜ ๋‘ ๊ฐ€์ง€ ํ•จ์ˆ˜๋ฅผ ์•Œ์•„๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

exactInput

์ด ํ•จ์ˆ˜๋Š” ์œ ๋™์„ฑ ํ’€๋กœ ๋“ค์–ด์˜ฌ ํ† ํฐ์˜ ์ •ํ™•ํ•œ ์ˆ˜๋Ÿ‰์„ ๊ฐ€์ง€๊ณ  ์œ ๋™์„ฑ ํ’€์—์„œ ๋‚˜๊ฐˆ ํ† ํฐ์˜ ์ž„์˜ ์ˆ˜๋Ÿ‰์„ ๋ฐ›๋„๋ก ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ ๊ตํ™˜๋  ํ† ํฐ์— ๋Œ€ํ•ด ํ™•์ •์ ์ธ ์ˆ˜๋Ÿ‰์„ ๋ฐ›์„ ์ˆ˜ ์—†๋Š” ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ํ† ํฐ ๊ตํ™˜ ํŠธ๋žœ์žญ์…˜์„ ๋ณด๋‚ด๊ณ  ๋‚˜์„œ ํ•ด๋‹น ํŠธ๋žœ์žญ์…˜์ด ๋ธ”๋ก์— ๋“ค์–ด๊ฐ€๊ธฐ ์ „๊นŒ์ง€, ์•ž์„œ ๋‹ค๋ฅธ ํŠธ๋žœ์žญ์…˜๋“ค๋กœ ์ธํ•ด ํ† ํฐ์˜ ๊ตํ™˜์ด ์ง€์†์ ์œผ๋กœ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค. ์ดํ›„์— ๋‚˜์˜ ํŠธ๋žœ์žญ์…˜์ด ๋ธ”๋ก์— ๋“ค์–ด๊ฐˆ ๋•Œ์—๋Š” ์œ ๋™์„ฑ ํ’€ ๋‚ด๋ถ€์˜ ํ† ํฐ ๋น„์œจ์— ๋ณ€๋™์ด ์ƒ๊ธฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์œ ๋™์„ฑ ํ’€์˜ ํ† ํฐ ์ˆ˜๋Ÿ‰์ด ๋ณ€๊ฒฝ๋˜๋Š” ๊ฒƒ์„ ์Šฌ๋ฆฌํ”ผ์ง€(Slippage)๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด, ์œ ๋™์„ฑ ํ’€์—์„œ ๋‚˜๊ฐ€๋Š” ํ† ํฐ์— ๋Œ€ํ•ด ์ž„์˜ ์ˆ˜๋Ÿ‰์„ ์ง€์ •ํ•  ์ˆ˜๋ฐ–์— ์—†๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค.

import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";

address UNIV3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;

ISwapRouter v3router = ISwapRouter(UNIV3_ROUTER);

uint256 tokenOut = v3router.exactInput(
    ISwapRouter.ExactInputParams({
        path: abi.encodePacked(address(token0), fee, address(token1)),
        recipient: address(this),
        deadline: block.timestamp,
        amountIn: 1e18,
        amountOutMinimum: 0
    })
);

ํ•จ์ˆ˜๊ฐ€ ๋ฐ›๋Š” ์ธ์ž๋ถ€ํ„ฐ ํ™•์ธํ•˜๋ฉด, ExactInputParams ๋ผ๋Š” ๊ตฌ์กฐ์ฒด๋ฅผ ์ด์šฉํ•˜์—ฌ ์ธ์ž๋“ค์„ ๊ตฌ์„ฑํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ–ˆ์„ ๋•Œ์˜ ์ด์ ์€, ๊ตฌ์กฐ์ฒด์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ๊ฐ€ ์ตœ์†Œํ•œ์˜ ํŒจ๋”ฉ์œผ๋กœ ์••์ถ•๋˜๋ฉฐ, ์ธ์ž๋ฅผ calldata๋กœ ๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ์ธ์ž๋ฅผ ๋ฉ”๋ชจ๋ฆฌ๋กœ ๋ณต์‚ฌํ•˜์ง€ ์•Š์•„๋„ ๋˜์–ด ํ• ๋‹น ๋น„์šฉ์„ ์•„๋‚„ ์ˆ˜ ์žˆ๋‹ค๋Š” ํŠน์ง•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

์ฒซ ๋ฒˆ์งธ ์ธ์ž๋ฅผ ๋ณด๋ฉด, path๊ฐ€ ์žˆ๋Š”๋ฐ ์œ ๋™์„ฑ ํ’€๋กœ ๋“ค์–ด์˜ฌ ํ† ํฐ์˜ ์ฃผ์†Œ, ์ˆ˜์ˆ˜๋ฃŒ, ๊ทธ๋ฆฌ๊ณ  ์œ ๋™์„ค ํ’€์—์„œ ๋‚˜๊ฐˆ ํ† ํฐ์˜ ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•˜์—ฌ abi.encodePacked ๋กœ ์••์ถ•ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ๋กœ recipient๋Š” ๊ตํ™˜ํ•ด ๋‚ด๋Š” ํ† ํฐ์„ ๋ฐ›์„ ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ณณ์ž…๋‹ˆ๋‹ค. ํ•ด๋‹น ์ฝ”๋“œ๊ฐ€ ์ปจํŠธ๋ž™ํŠธ์—์„œ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— address(this)๋กœ ์ง€์ •๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์„ธ ๋ฒˆ์งธ๋กœ deadline์€ ํ•ด๋‹น ํŠธ๋žœ์žญ์…˜์ด ๋ธ”๋ก์— ๋“ค์–ด๊ฐˆ ์ตœ๋Œ€ ์‹œ๊ฐ„์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. deadline์„ 30๋ถ„ ๋’ค๋กœ ์„ค์ •ํ•˜๊ณ , ํŠธ๋žœ์žญ์…˜์ด 30๋ถ„ ๋’ค์— ๋ธ”๋ก์— ๋“ค์–ด๊ฐ„๋‹ค๋ฉด ํ† ํฐ ๊ตํ™˜์€ ์‹คํŒจํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์˜ค๋žœ ๊ธฐ๊ฐ„์ด ์ง€๋‚ฌ์„ ๋•Œ ๊ฐ€๊ฒฉ ๋ณ€๋™์„ ์˜ˆ์ธกํ•˜๊ธฐ ์–ด๋ ต๊ธฐ ๋•Œ๋ฌธ์— ์‹คํŒจ๋ฅผ ํ•˜๋„๋ก ์ตœ์†Œํ•œ์˜ ์„ฑ๊ณต ์‹œ๊ฐ„์„ ์ž…๋ ฅํ•˜๋ผ๊ณ  ๋‘” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ €ํฌ์˜ ๊ฒฝ์šฐ ๋ณ„๋„๋กœ ์„ค์ •ํ•˜์ง€ ์•Š๊ณ , ํŠธ๋žœ์žญ์…˜์ด ๋ธ”๋ก์— ๋“ค์–ด๊ฐ€๋Š” ์‹œ๊ฐ„์ด deadline์œผ๋กœ ๊ตฌ์„ฑ๋˜๋„๋ก ํ–ˆ์Šต๋‹ˆ๋‹ค.

๋„ค ๋ฒˆ์งธ amountIn์€, token0์˜ ํ† ํฐ์ด ์œ ๋™์„ฑ ํ’€๋กœ ๋“ค์–ด์˜ฌ ์ˆ˜๋Ÿ‰์ž…๋‹ˆ๋‹ค. ๋ฐ”๋กœ ๋‹ค์Œ amountOutMinimum์„ 0์œผ๋กœ ์„ค์ •ํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๊ฒƒ์€ ์Šฌ๋ฆฌํ”ผ์ง€๋ฅผ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š๊ฒ ๋‹ค๋Š” ์˜๋ฏธ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ 1๊ฐœ์˜ ํ† ํฐ์œผ๋กœ ์ตœ์†Œ 10๊ฐœ์˜ ํ† ํฐ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์Šฌ๋ฆฌํ”ผ์ง€๋ฅผ ์ƒ๊ฐํ•˜์—ฌ 9.998๊ฐœ ์ •๋„ ๋ฐ›์•„๋„ ์ƒ๊ด€์ด ์—†๋‹ค๋ฉด, ํ•ด๋‹น ์˜์—ญ์— 9.998์„ ์ž…๋ ฅํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ํ•ด์„œ ํ† ํฐ์˜ ๊ตํ™˜์ด ์™„๋ฃŒ๋˜๋ฉด ์‹ค์ œ๋กœ ๊ตํ™˜๋œ ํ† ํฐ์˜ ์ˆ˜๋Ÿ‰์ด tokenOut์ด๋ผ๋Š” ์ˆซ์ž๋กœ ๋ฐ˜ํ™˜๋˜๊ธฐ ๋•Œ๋ฌธ์— ์‰ฝ๊ฒŒ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

exactOutput

์ด ํ•จ์ˆ˜๋Š” exactInput๊ณผ ์™„์ „ํžˆ ๋ฐ˜๋Œ€์˜ ๊ฒฝ์šฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ•จ์ˆ˜๊ฐ€ ๋œปํ•˜๋Š” ๋ฐ” ๊ทธ๋Œ€๋กœ ์œ ๋™์„ฑ ํ’€์—์„œ ๋‚˜๊ฐˆ ํ† ํฐ์˜ ์ •ํ™•ํ•œ ์ˆ˜๋Ÿ‰์„ ๊ณ ์ •ํ•ด๋‘๊ณ  ๋“ค์–ด์˜ฌ ํ† ํฐ์— ๋Œ€ํ•ด ์ž„์˜ ์ˆ˜๋Ÿ‰์„ ๋ฐ›๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol";

address UNIV3_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564;

ISwapRouter v3router = ISwapRouter(UNIV3_ROUTER);

uint256 tokenIn = v3router.exactOutput(
    ISwapRouter.ExactOutputParams({
        path: abi.encodePacked(address(token1), fee, address(token0)),
        recipient: address(this),
        deadline: block.timestamp,
        amountInMaximum: 2e18,
        amountOut: 1e18
    })
);

recipient์™€ deadline๋Š” ๋™์ผํ•œ ์—ญํ• ์„ ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, path์˜ ๊ฒฝ์šฐ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์—์„œ๋Š” ์™ผ์ชฝ๋ถ€ํ„ฐ ์œ ๋™์„ฑ ํ’€์—์„œ ๋‚˜๊ฐˆ ํ† ํฐ ์ฃผ์†Œ, ์ˆ˜์ˆ˜๋ฃŒ, ์œ ๋™์„ฑ ํ’€๋กœ ๋“ค์–ด์˜ฌ ํ† ํฐ ์ฃผ์†Œ ์ˆœ์„œ๋กœ ์ด๋ค„์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. ๋“ค์–ด์˜ฌ ํ† ํฐ์€ ์˜ค๋ฅธ์ชฝ์— ์œ„์น˜ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์•„๋ž˜ ๋‹ค๋ฅธ ์˜์—ญ amountInMaximum์€ ์•„๋ž˜ amountOut ์ˆ˜๋Ÿ‰๋งŒํผ ๊ตํ™˜๋  ๋•Œ ์ตœ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ˆ˜๋Ÿ‰์„ ์ ์–ด๋‘” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ดํ›„์— ์Šค์™‘์ด ์™„๋ฃŒ๋˜๋ฉด, ์‹ค์ œ๋กœ ๋“ค์–ด๊ฐ„ ํ† ํฐ ์ˆ˜๋Ÿ‰์ด tokenIn ์œผ๋กœ ๋ฐ˜ํ™˜๋˜์–ด ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Multihop Swap

ํ† ํฐ์„ ๊ตํ™˜์„ ํ•˜๋Š” ๋ฐ ์žˆ์–ด์„œ๋Š” ๋‹ค์–‘ํ•œ ๊ฒฝ์šฐ๋“ค์„ ์ƒ์ •ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, bean ํ† ํฐ์„ ๊ตฌํ•˜๋ ค๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์žˆ๋Š”๋ฐ, DAI๋งŒ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ bean์€ ETH์™€ ์Œ์œผ๋กœ ๊ตฌ์„ฑ๋œ ์œ ๋™์„ฑ ํ’€๋ฐ–์— ์—†์–ด์„œ ์ด ์‚ฌ์šฉ์ž๋Š” DAI๋ฅผ ETH๋กœ ๊ตํ™˜ํ•œ ๋‹ค์Œ, ๊ตํ™˜๋œ ETH๋ฅผ ๋‹ค์‹œ bean์œผ๋กœ ๊ตํ™˜ํ•˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ๊ฐ๊ฐ ํŠธ๋žœ์žญ์…˜์œผ๋กœ ๊ตฌ์„ฑํ•˜๋ฉด, ๊ฐ€์Šค๋น„๋„ ์•„๊น๊ณ  ํ† ํฐ์ด ๊ตํ™˜๋˜๋Š” ๋™์•ˆ ์Šฌ๋ฆฌํ”ผ์ง€๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜๋„ ์žˆ๊ธฐ์— ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌ๋˜๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์œ ๋™์„ฑ ํ’€๋“ค์„ ์—ฐ๊ฒฐํ•ด ์ฃผ๋Š” ๊ธฐ๋Šฅ์ด V2์—๋„ ์žˆ์—ˆ๊ณ , V3์—๋„ ์žˆ๊ธฐ์—, path๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์•„์ฃผ ์‰ฝ๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ExactInputParams ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ ์šฉํ•œ๋‹ค๋ฉด, ์ž…๋ ฅ ํ† ํฐ์ธ DAI์™€ ๊ตฌ์„ฑ๋˜์–ด ์žˆ๋Š” ETH ์œ ๋™์„ฑํ’€, ๊ทธ๋ฆฌ๊ณ  ETH์™€ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ๋Š” bean ์œ ๋™์„ฑ ํ’€์„ ์ฐพ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์ด, ์ด๋”๋ฆฌ์›€ ํ’€๊ณผ, ์ด๋”๋ฆฌ์›€ ๋นˆ ํ’€ ์‚ฌ์ด์—์„œ ์ด๋”๋ฆฌ์›€์ด๋ผ๋Š” ๊ณตํ†ต ์œ ๋™์„ฑ์„ ๊ณต์œ ํ•˜๋Š” ๋ชจ์Šต
๋‘ ๊ฐ€์ง€ ํ’€์„ ์ด์šฉํ•˜์—ฌ, DAI๋ฅผ Bean์œผ๋กœ, Bean์„ Dai๋กœ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๋„๋ก ์œ ๋™์„ฑ์„ ์—ฐ๊ฒฐํ•˜๋Š” ์˜ˆ์‹œ
...
    path: abi.encodePacked(address(DAI), fee, address(WETH), fee, address(BEAN)),
...

์ด๋ ‡๊ฒŒ ํŒจ์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๋ฉด, DAI/ETH ์œ ๋™์„ฑ ํ’€์— DAI๊ฐ€ ๋“ค์–ด๊ฐ€๊ณ  ETH๊ฐ€ ETH/BEAN ์œ ๋™์„ฑ ํ’€๋กœ ๋“ค์–ด๊ฐ€, ์ตœ์ข…์ ์œผ๋กœ bean์ด ๊ตํ™˜๋˜์–ด ๋‚˜๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์œ ๋™์„ฑ์„ ์ œ๊ฑฐํ•˜๊ณ , ์Œ“์ธ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ฒญ๊ตฌํ•˜๊ธฐ

์œ ๋‹ˆ์Šค์™‘์— ๋“ฑ๋ก๋œ ์œ ๋™์„ฑ์€ ๋“ฑ๋กํ•œ recipient๋งŒ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ๋™์ผํ•œ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์— ๋‹ค์–‘ํ•œ ์ด์šฉ์ž๋“ค์ด ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•˜๋Š” ์ผ์ด ๋Š˜ ์žˆ๊ธฐ์—, ๊ฐ๊ฐ์˜ ์œ ๋™์„ฑ์— ๊ณ ์œ ํ•œ ํ‚ค๋ฅผ ๋ถ™์—ฌ ๊ด€๋ฆฌํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํฌ์ง€์…˜ ํ‚ค๋ผ๊ณ  ํ•˜๋ฉฐ, ์ด๋ฅผ ๊ณ„์‚ฐํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

import {PositionKey} from "@uniswap/v3-periphery/contracts/libraries/PositionKey.sol";

bytes32 positionKey = PositionKey.compute(address(this), lowerTick, upperTick);

ํฌ์ง€์…˜ ํ‚ค๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ์ธ์ž๋กœ, ์œ ๋™์„ฑ ๊ณต๊ธ‰์ž์˜ ์ฃผ์†Œ, ๋‚ฎ์€ Tick, ๋†’์€ Tick์„ ์ด์šฉํ•˜์—ฌ ๊ณ„์‚ฐํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‚ด๋ถ€์ ์œผ๋กœ ํ•ด์‹œ ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•˜๋ฏ€๋กœ ๊ณ„์‚ฐ๋˜๋Š” ํ‚ค๋Š” ์œ ์ผํ•œ ๊ฐ’์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ณ„์‚ฐ๋œ ํฌ์ง€์…˜ ํ‚ค๋ฅผ ์ด์šฉํ•˜์—ฌ ์œ ๋™์„ฑ ํ’€์— ๋“ฑ๋ก๋œ ์œ ๋™์„ฑ์— ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋˜๋ฉฐ, ์ด๋ฅผ ์ง์ ‘์ ์œผ๋กœ ํ•ด์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// ํ•ด๋‹นํ•˜๋Š” ํฌ์ง€์…˜์— ์กด์žฌํ•˜๋Š” ์œ ๋™์„ฑ ์ˆ˜๋Ÿ‰ ์กฐํšŒ
(uint128 Liquidity, , , , ) = pool.positions(positionKey);

// ํ•ด๋‹น ํฌ์ง€์…˜์—์„œ ์œ ๋™์„ฑ ๋งŒํผ ํ† ํฐ์„ ์ œ์™ธํ•˜๋ฉฐ, ๊ฐ ํ† ํฐ ๋งˆ๋‹ค ์œ ๋™์„ฑ ์ œ๊ฑฐ๋˜๋Š” ์ˆ˜๋Ÿ‰์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
(uint256 amount0, uint256 amount1) = pool.burn(lowerTick, upperTick, Liquidity);

// ์ œ์™ธ๋œ ํ† ํฐ ์ˆ˜๋Ÿ‰ ์ „๋ถ€๋ฅผ ์ˆ˜์ง‘ํ•˜์—ฌ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
(amount0, amount1) = pool.collect(address(this), lowerTick, upperTick, uint128(amount0), uint128(amount1));

burn ์„ ํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์œ ๋™์„ฑ์ด ๋˜๋Œ๋ ค์ง€๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋“ฑ๋ก๋œ ์œ ๋™์„ฑ์ด ์ œ์™ธ๋œ ์ƒํƒœ๋กœ ๋‚จ์•„์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„์— collect ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์œผ๋กœ ์ œ์™ธ๋œ ์œ ๋™์„ฑ์„ ํ˜ธ์ถœ ๋‹น์‚ฌ์ž์—๊ฒŒ ์ „์†กํ•˜๋„๋ก ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์œ ๋‹ˆ์Šค์™‘ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ํ† ํฐ ์ˆ˜์ˆ˜๋ฃŒ๊ฐ€ ๋ˆ„์ ๋˜์–ด ์žˆ๋Š” ๋ชจ์Šต๊ณผ ์ด์— ๋”ฐ๋ฅธ ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ง•์ˆ˜ํ•˜๋Š” ๋ชจ์Šต
์œ ๋‹ˆ์Šค์™‘ ์ธํ„ฐํŽ˜์ด์Šค์—์„œ ์Œ“์ธ ์ˆ˜์ˆ˜๋ฃŒ

์œ ๋‹ˆ์Šค์™‘ V2์™€ V3์˜ ์—„์ฒญ๋‚œ ์ฐจ์ด๋ฅผ ๋ณด์ด๋Š” ๋ถ€๋ถ„์ด ์ˆ˜์ˆ˜๋ฃŒ์˜ ์ฒ˜๋ฆฌ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. V2์˜ ๊ฒฝ์šฐ ์ˆ˜์ˆ˜๋ฃŒ๊ฐ€ ์œ ๋™์„ฑ์— ์ž๋™์œผ๋กœ ๋ˆ„์ ๋˜๋Š” ๋ฐ˜๋ฉด, V3์˜ ๊ฒฝ์šฐ ์ˆ˜์ˆ˜๋ฃŒ๋Š” ์œ ๋™์„ฑ์— ๋”ํ•ด์ง€์ง€ ์•Š๊ณ  ์ œ์™ธ๋œ ์ƒํƒœ๋กœ ๋‚จ์•„์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์œ ๋™์„ฑ์ด ์ œ๊ฑฐ๋˜์–ด ์‚ฌ์šฉ๋˜์ง€ ์•Š๋Š” ์œ ๋™์„ฑ์œผ๋กœ ์ œ์™ธ๋˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ฐ๊ฐ์˜ ์ž…๋ ฅ๊ฐ’์„ ์ตœ๋Œ€๋กœ ์ž…๋ ฅํ•˜๋Š” ๊ฒƒ์œผ๋กœ, ์ œ์™ธ๋œ ์œ ๋™์„ฑ ์ „์ฒด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

(amount0, amount1) = pool.collect(address(this), lowerTick, upperTick, type(uint128).max, type(uint128).max);

์œ ๋‹ˆ์Šค์™‘์˜ ๋™์ž‘ ๊ตฌ์กฐ์™€ ์ตœ์ข… ์ฝ”๋“œ

์ €ํฌ๊ฐ€ ์ž‘์„ฑํ•œ ์ปจํŠธ๋ž™ํŠธ๋Š” ์œ ๋‹ˆ์Šค์™‘ ์ปจํŠธ๋ž™ํŠธ์™€ ์ง์ ‘์ ์œผ๋กœ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ์ž๋“ค์€ ๐ŸฆŠ Metamask์™€ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ๊ณต๊ฐœํ‚ค-๋น„๋ฐ€ํ‚ค ๊ธฐ๋ฐ˜์˜ ์ง€๊ฐ‘์„ ์‚ฌ์šฉํ•˜๋ฉฐ, ์œ ๋™์„ฑ ํ’€๊ณผ ์ง์ ‘ ์†Œํ†ตํ•˜๋Š” ๋Œ€์‹ ์— ์œ ๋‹ˆ์Šค์™‘ ์ธํ„ฐํŽ˜์ด์Šค์— ์—ฐ๊ฒฐ๋œ ํ† ํฐ์„ ๊ตํ™˜ํ•˜๊ธฐ ์œ„ํ•ด ๋งŒ๋“ค์–ด์ง„ SwapRouter, ์œ ๋™์„ฑ ํ’€์— ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๋Š” NonFungiblePositionManager ์„ ์‚ฌ์šฉํ•˜๊ฒŒ ๋˜๋ฉฐ, ์ด๋Ÿฌํ•œ ๊ฒƒ๋“ค์„ Periphery ์ปจํŠธ๋ž™ํŠธ๋ผ๊ณ  ๋ถ€๋ฅด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

Periphery ์ปจํŠธ๋ž™ํŠธ๋“ค์€ ์œ ๋™์„ฑ ํ’€๊ณผ ๊ฐ™์€ Core ์ปจํŠธ๋ž™ํŠธ์— ๋ฐ˜์‘ํ•˜๊ธฐ ์œ„ํ•ด callback ํ•จ์ˆ˜๋“ค์„ ๊ตฌํ˜„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ๊ฐ์˜ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ํ† ํฐ์ด ๊ตํ™˜๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” uniswapV3SwapCallback ๊ทธ๋ฆฌ๊ณ  ์œ ๋™์„ฑ์ด ๋“ฑ๋ก๋  ๋•Œ ํ˜ธ์ถœ๋˜๋Š” uniswapV3MintCallback ์ด ๊ตฌํ˜„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž๊ฐ€ Periphery ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ† ํฐ ๊ตํ™˜์„ ์š”์ฒญํ•˜์—ฌ, Periphery ์ปจํŠธ๋ž™ํŠธ๊ฐ€ Core์ธ ์œ ๋™์„ฑ ํ’€์„ ํ˜ธ์ถœํ•˜์—ฌ ํ† ํฐ์„ ๊ตํ™˜ํ•˜๋Š” ๊ฐœ์š”๋„
Periphery ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์ค‘๊ฐœํ•˜๋Š” Core ์ปจํŠธ๋ž™ํŠธ ๊ฐœ์š”๋„

Periphery ์ปจํŠธ๋ž™ํŠธ ๋˜ํ•œ Core ์ปจํŠธ๋ž™ํŠธ์˜ ์ธก๋ฉด์—์„œ ๋ณด์ž๋ฉด ๋ณ„๋„์˜ ์ปจํŠธ๋ž™ํŠธ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ, ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•œ๋‹ค๋Š” ๊ฒƒ์€ ์ฝ”์–ด ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ์ฝœ๋ฐฑ์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋ ‡๋‹ค๋ฉด, ์ฝœ๋ฐฑ์„ ํ†ตํ•ด์„œ ์–ด๋–ค ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์„๊นŒ์š”? ์œ ๋‹ˆ์Šค์™‘์€ ์ฝœ๋ฐฑ์ด ๋‹ค์–‘ํ•œ ๋กœ์ง์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๋„๋ก ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์„œ ์ €ํฌ๋Š” callback์˜ ์„ธ ๋ฒˆ์งธ ์ธ์ž๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•„ ์ด๋ฆ„์„ ์ง€์ •ํ•˜์ง€ ์•Š์•˜์—ˆ์Šต๋‹ˆ๋‹ค.

function uniswapV3MintCallback(
    uint256 amount0Owed,
    uint256 amount1Owed,
    bytes calldata // <<--- ์ด ๋ถ€๋ถ„
) external

์–ธ๊ธ‰ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ, ํ•ด๋‹น ๋ถ€๋ถ„์€ mint ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋  ๋•Œ ๋งˆ์ง€๋ง‰ ์ธ์ž๋ฅผ ๊ทธ๋Œ€๋กœ callback์œผ๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ์—ญํ• ์„ ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. bytes("")๋กœ ๋นˆ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ–ˆ์—ˆ์ง€์š”, ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์ฑ„์›Œ ์œ ๋™์„ฑ ํ’€๋กœ ์ „์†กํ•œ ๋‹ค์Œ, ์œ ๋™์„ฑ ํ’€์ด Periphery ์ปจํŠธ๋ž™ํŠธ์˜ callback ํ•จ์ˆ˜๋กœ ๋ณด๋‚ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋กœ์ง์˜ ๋ถ„๋ฆฌ๋Š” ์ฝ”์–ด ์ปจํŠธ๋ž™ํŠธ์˜ ์˜์กด์„ฑ์ด ์ตœ์†Œํ™”๋œ ์ƒํƒœ์—์„œ ํ…Œ์ŠคํŠธ ๊ฐ€๋Šฅ์„ฑ, ๊ทธ๋ฆฌ๊ณ  ์‚ฌ์šฉ์ž๊ฐ€ ๊ตฌ์„ฑ ๊ฐ€๋Šฅํ•œ ๊ฐœ๋ฐœ ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์•ž์„œ ์ž‘์„ฑํ•œ ์ปจํŠธ๋ž™ํŠธ ์ฝ”๋“œ๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ํ† ํฐ์„ ์œ ๋™์„ฑ ๊ณต๊ธ‰์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ์‚ฌ์šฉ์ž์˜ ์ง€๊ฐ‘์— ์žˆ๋Š” ํ† ํฐ์„ ์œ ๋™์„ฑ ๊ณต๊ธ‰ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ๋‹น์—ฐํžˆ ์‚ฌ์šฉ์ž์˜ ํ† ํฐ์€ ์ €ํฌ๊ฐ€ ์ž‘์„ฑํ•˜๋Š” ์ปจํŠธ๋ž™ํŠธ ์ฝ”๋“œ์— approve๊ฐ€ ๋˜์–ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    (uint256 amount0, uint256 amount1) = pool.mint(
        address(this),
        lowerTick,
        upperTick,
        liquidity,
        abi.encode(msg.sender) // ํ•ด๋‹น ์ฝ”๋“œ์˜ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ด์šฉํ•˜๊ณ  ์žˆ๋Š” ์ง€๊ฐ‘์ฃผ์†Œ๋ฅผ bytes๋กœ ์ธ์ฝ”๋”ฉํ•˜์—ฌ ์ „๋‹ฌ
    );
...

// Pool์—์„œ ์ปจํŠธ๋ž™ํŠธ๋กœ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜, 
// ํ•ด๋‹น ํ•จ์ˆ˜์˜ ํ˜ธ์ถœ์ž๋Š” Pool์ด๋ฉฐ ๋ฐ์ดํ„ฐ๋Š” Pool์—์„œ ์ „์†ก๋ฉ๋‹ˆ๋‹ค.
function uniswapV3MintCallback(
    uint256 amount0Owed,
    uint256 amount1Owed,
    bytes calldata data // mint๋ฅผ ํ†ตํ•ด ์ „๋‹ฌํ•œ data๊ฐ€ ์ด๊ณณ์œผ๋กœ ๋„˜์–ด์˜ต๋‹ˆ๋‹ค.
) external {
    address from = abi.decode(data, (address)); // ๋„˜์–ด์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์†Œ ํƒ€์ž…์œผ๋กœ ๋””์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.
    // ์ดํ›„์— ๋””์ฝ”๋”ฉ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ, ์‚ฌ์šฉ์ž์˜ ์ง€๊ฐ‘์—์„œ ์œ ๋™์„ฑ ํ’€๋กœ ํ† ํฐ์„ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.
    if (amount0Owed != 0) token0.transferFrom(from, address(pool), amount0Owed);
    if (amount1Owed != 0) token1.transferFrom(from, address(pool), amount1Owed);
}

์—ฌ๊ธฐ์—์„œ data๋Š” bytes ํƒ€์ž…์œผ๋กœ๋งŒ ์ „์†ก๋˜๋ฏ€๋กœ, ์ž…๋ ฅ๋˜๋Š” ๋ฐ์ดํ„ฐ ํƒ€์ž…๊ณผ ์ฝœ๋ฐฑ์—์„œ ํ•ด์„๋˜์–ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ ํƒ€์ž…์ด ์ผ์น˜ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ์˜ ์ฝ”๋“œ์—์„œ๋Š” ๋А์Šจํ•˜๊ฒŒ ํƒ€์ž…์„ ๊ณต์œ ํ•˜๊ณ  ์žˆ์ง€๋งŒ, ๊ตฌ์กฐ์ฒด๋ฅผ ์ด์šฉํ•ด์„œ ํƒ€์ž…์— ๋Œ€ํ•œ ์‹ค๋งˆ๋ฆฌ๋ฅผ ๊ณต์œ ํ•˜๋Š” ๊ฒƒ์ด ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋ ‡๊ฒŒ ์œ ๋‹ˆ์Šค์™‘์˜ V3 ์ปจํŠธ๋ž™ํŠธ๋“ค์ด ๋…๋‹จ์ ์œผ๋กœ ๋™์ž‘ํ•˜๋Š” ๋ฐฉ์‹๋ณด๋‹ค, ๋‹ค๋ฅธ ์ปจํŠธ๋ž™ํŠธ์™€ ๋ณด๋‹ค ๋ช…ํ™•ํ•˜๊ฒŒ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„๊ฐ€ ๋˜์—ˆ๋‹ค๋Š” ์ ์„ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด, ์œ ๋‹ˆ์Šค์™‘์„ ๋ณด๋‹ค ํŽธํ•˜๊ฒŒ ํ†ตํ•ฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒ๋‹ˆ๋‹ค. ์ง€๊ธˆ๊นŒ์ง€ ์ž‘์„ฑํ•œ ์ „์ฒด ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด ๋ด…์‹œ๋‹ค.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
import "@uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol";
import "@uniswap/v3-core/contracts/libraries/TickMath.sol";
import "@uniswap/v3-core/contracts/interfaces/callback/IUniswapV3MintCallback.sol";
import "@uniswap/v3-periphery/contracts/libraries/LiquidityAmounts.sol";
import "@beandao/contracts/interfaces/IERC20.sol";

/**
 * @title UniswapV3Integration
 * @author yoonsung.eth
 * @notice ํ† ํฐ์„ ๋‘ ๊ฐœ์— ํ•ด๋‹นํ•˜๋Š” ์œ ๋™์„ฑ ํ’€์„ ์ƒ์„ฑํ•˜์—ฌ ์ดˆ๊ธฐ ๊ฐ€๊ฒฉ์„ ๊ฒฐ์ •ํ•˜์—ฌ ์ดˆ๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.
 * ์‚ฌ์šฉ์ž๋Š” ํ† ํฐ์„ ํ•ด๋‹น ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ†ตํ•˜์—ฌ ๋ฏธ๋ฆฌ ์ง€์ •๋œ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์— ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•˜์—ฌ
 * ์‚ฌ์šฉ์ž๋“ค์ด NFT์—†์ด ์œ ๋™์„ฑ์„ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ์ปจํŠธ๋ž™ํŠธ์ž…๋‹ˆ๋‹ค.
 * @dev ํ† ํฐ๊ณผ ์ดˆ๊ธฐ ๊ฐ€๊ฒฉ ๊ณต๊ฐ„์— ๋Œ€ํ•œ ์ •๋ณด ๋ฐ ์ˆ˜์ˆ˜๋ฃŒ ์ •๋ณด๊ฐ€ constructor์™€ ์ปจํŠธ๋ž™ํŠธ์— ์ •์˜๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, 
 * ํ•ด๋‹น ์ •๋ณด๋“ค์„ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๋ฐฐํฌ๋  ๋•Œ ์ดˆ๊ธฐํ™” ๋˜๋„๋ก ํ•˜๋ฉด ์ข€ ๋” ์œ ์—ฐํ•ด์ง‘๋‹ˆ๋‹ค.
 */
contract UniswapV3Integration is IUniswapV3MintCallback {
    address public constant UNIV3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984;
    uint24 constant fee = 3000;
    int24 constant tickSpacing = 60;
    uint256 constant PRECISION = 2**96;

    IUniswapV3Factory v3Factory = IUniswapV3Factory(UNIV3_FACTORY);
    IUniswapV3Pool pool;
    IERC20 token0;
    IERC20 token1;
    int24 lowerTick;
    int24 upperTick;

    mapping(address => uint128) userLiquidity;

    constructor(address _token0, address _token1) {
        token0 = IERC20(_token0);
        token1 = IERC20(_token1);
        pool = IUniswapV3Pool(v3Factory.createPool(address(_token0), address(_token1), fee));

        uint160 initialPriceX96 = encodeSqrtPrice(address(_token0), address(_token1), 1e18, 0.01 ether);
        uint160 upperPriceX96 = encodeSqrtPrice(address(_token0), address(_token1), 1e18, 10 ether);

        pool.initialize(initialPriceX96);

        lowerTick = TickMath.getTickAtSqrtRatio(initialPriceX96) + tickSpacing;
        upperTick = TickMath.getTickAtSqrtRatio(upperPriceX96);

        if ((lowerTick % tickSpacing) != 0)
            lowerTick = lowerTick - (lowerTick % tickSpacing) + (lowerTick < 0 ? -tickSpacing : tickSpacing);
        if ((upperTick % tickSpacing) != 0)
            upperTick = upperTick - (upperTick % tickSpacing) + (upperTick < 0 ? -tickSpacing : tickSpacing);

        require(upperTick > lowerTick);
    }

    /**
     * @notice ์œ ๋™์„ฑ ํ’€์— ๋Œ€ํ•œ ํ† ํฐ ๋‘ ๊ฐœ๋ฅผ ์˜ˆ์น˜ํ•˜๋ฉฐ, ์‚ฌ์šฉ์ž๋Š” ํ•ด๋‹น ์ปจํŠธ๋ž™ํŠธ์— ๋Œ€ํ•ด ํ† ํฐ ์‚ฌ์šฉ ๊ถŒํ•œ์„ ํ—ˆ์šฉํ•ด ์ฃผ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
     * @param token0Amount token0 ์˜ ์˜ˆ์น˜ ์ˆ˜๋Ÿ‰
     * @param token1Amount token1 ์˜ ์˜ˆ์น˜ ์ˆ˜๋Ÿ‰
     */
    function deposit(uint256 token0Amount, uint256 token1Amount) external {
        (uint160 sqrtPriceX96, , , , , , ) = pool.slot0();

        uint128 liquidity = LiquidityAmounts.getLiquidityForAmounts(
            sqrtPriceX96,
            TickMath.getSqrtRatioAtTick(lowerTick),
            TickMath.getSqrtRatioAtTick(upperTick),
            token0Amount,
            token1Amount
        );

        (uint256 amount0, uint256 amount1) = pool.mint(
            address(this),
            lowerTick,
            upperTick,
            liquidity,
            abi.encode(msg.sender)
        );

        userLiquidity[msg.sender] += liquidity;
    }

    /**
     * @notice ์ด๋ฏธ ์˜ˆ์น˜๋œ ์œ ๋™์„ฑ์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.
     * @param liquidity ์ œ๊ฑฐํ•  ์œ ๋™์„ฑ ์ˆ˜๋Ÿ‰
     */
    function withdraw(uint128 liquidity) external returns (uint256 amount0, uint256 amount1) {
        userLiquidity[msg.sender] -= liquidity;
        (amount0, amount1) = pool.burn(lowerTick, upperTick, liquidity);
        (amount0, amount1) = pool.collect(msg.sender, lowerTick, upperTick, uint128(amount0), uint128(amount1));
    }

    // ์—ฌ๊ธฐ์— ์‚ฌ์šฉ์ž๋งˆ๋‹ค ์ˆ˜์ˆ˜๋ฃŒ๋ฅผ ์ฒญ๊ตฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๊ตฌํ˜„ํ•ด๋ณด์„ธ์š”!

    /**
     * @notice ์œ ๋‹ˆ์Šค์™‘ ํ’€์— ์œ ๋™์„ฑ ๊ณต๊ธ‰ํ•  ๋•Œ ํ’€์—์„œ ์˜ˆ์น˜๋˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜, IUniswapV3MintCallback์— ์˜ํ•ด ๊ตฌํ˜„๋ฉ๋‹ˆ๋‹ค.
     */
    function uniswapV3MintCallback(
        uint256 amount0Owed,
        uint256 amount1Owed,
        bytes calldata data
    ) external {
        address from = abi.decode(data, (address));
        if (amount0Owed != 0) token0.transferFrom(from, address(pool), amount0Owed);
        if (amount1Owed != 0) token1.transferFrom(from, address(pool), amount1Owed);
    }

    function encodeSqrtPrice(
        address addr0,
        address addr1,
        uint256 reserve0,
        uint256 reserve1
    ) internal pure returns (uint160 sqrtPriceX96) {
        if (addr0 > addr1) {
            sqrtPriceX96 = uint160(sqrt((reserve0 * PRECISION * PRECISION) / reserve1));
        } else {
            sqrtPriceX96 = uint160(sqrt((reserve1 * PRECISION * PRECISION) / reserve0));
        }
    }

    function sqrt(uint256 x) internal pure returns (uint256 y) {
        uint256 z = (x + 1) / 2;
        y = x;
        while (z < y) {
            y = z;
            z = (x / z + z) / 2;
        }
    }
}

๊ฐ€์žฅ ์ปค๋‹ค๋ž€ ๋ฌธ์ œ! ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š”โ€ฆ

์œ„์˜ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ฉํ•˜์—ฌ ๊ฐœ๋ฐœํ•˜๋ ค๊ณ  ๋ณด๋ฉด ๊ฐ€์žฅ ์ปค๋‹ค๋ž€ ๋ฌธ์ œ์— ๋ด‰์ฐฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋ฐ”๋กœ ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•œ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ ค๋ฉด, ์˜์กด์„ฑ ๋ฌธ์ œ๋กœ ์ธํ•ด ์†”๋ฆฌ๋””ํ‹ฐ ๋ฒ„์ „์„ 0.7.6์— ๋งž์ถฐ์•ผ ํ•œ๋‹ค๋Š” ๊ฑฐ์ฃ .

์œ ๋‹ˆ์Šค์™‘ ์ฝ”๋“œ๋ฅผ ์ด์šฉํ•˜์—ฌ ๋‚ด ์ฝ”๋“œ์— ํ†ตํ•ฉํ–ˆ์„ ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฅ˜ ๋ฉ”์‹œ์ง€
์‹ค์ œ ์œ ๋‹ˆ์Šค์™‘ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ฉํ•  ๋•Œ ์ƒ๊ธฐ๋Š” ์˜ค๋ฅ˜๋“ค

์œ ๋‹ˆ์Šค์™‘ ์ž์ฒด๋Š” ์ด๋ฏธ ๊ฐ ์ฒด์ธ์— ๋ฐฐํฌ๋œ ์ƒํƒœ์ด๊ธฐ ๋•Œ๋ฌธ์—, ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด์„œ ์ฝœ๋ฐฑ์„ ์ฃผ๊ณ ๋ฐ›์„ ์ˆ˜๋งŒ ์žˆ๋‹ค๋ฉด ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์–ด๋–ค ์ปดํŒŒ์ผ๋Ÿฌ๋กœ ์ปดํŒŒ์ผ ๋˜๋“ ์ง€ ์ƒ๊ด€์ด ์—†์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํŠนํžˆ๋‚˜, ์ตœ์‹  ๋ฒ„์ „์˜ ์†”๋ฆฌ๋””ํ‹ฐ ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์—๋Š”, ์ƒ์„ฑ๋˜๋Š” ํฌ๊ณ  ์ž‘์€ ๋ฐ”์ดํŠธ ์ฝ”๋“œ์˜ ๋ฒ„๊ทธ๊ฐ€ ์ ๊ณ , ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฌธ๋ฒ•๋“ค์ด ๋Œ€๊ฑฐ ์ถ”๊ฐ€๋˜๊ธฐ๋„ ํ•˜์˜€์œผ๋ฉฐ, ์ƒˆ๋กœ์šด Yul IR์ด ์ ์šฉ๋˜์–ด ์‹คํ–‰๋˜๋Š” ์ฝ”๋“œ๊ฐ€ ๋ณด๋‹ค ์ตœ์ ํ™”๋œ๋‹ค๋Š” ์ ์ด ํŠน์ง•์ž…๋‹ˆ๋‹ค.

์ €ํฌ๊ฐ€ ์œ ๋‹ˆ์Šค์™‘์„ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐํฌํ•  ๊ฒƒ๋„ ์•„๋‹ˆ๊ณ , ์ œ๊ณต๋˜๋Š” ์ธํ„ฐํŽ˜์ด์Šค์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•˜์—ฌ ๊ธฐ๋ฐฐํฌ๋œ ์œ ๋‹ˆ์Šค์™‘์„ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ๋˜๊ธฐ์—, ์ด๋Ÿฐ ๊ฒƒ๋“ค์€ ๊ทนํžˆ ์‚ฌ์†Œํ•œ ๋ถ€๋ถ„์ด๋ผ๋Š” ์ ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ, ์œ ๋‹ˆ์Šค์™‘ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ 0.8.11 ๋ฒ„์ „ ์ด์ƒ์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ ํŒจํ‚ค์ง€๋ฅผ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค. ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๊ณ , ๊ตฌ ๋ฒ„์ „ ์ปดํŒŒ์ผ๋Ÿฌ์—์„œ ์‚ฌ์šฉ๋˜๋˜ ๋А์Šจํ•œ ํƒ€์ž… ์ฒดํฌ๋ฅผ ์ข€ ๋” ํ™•์‹คํ•˜๊ฒŒ ์ˆ˜์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Uniswap V3 Pack on Github

ํ•ด๋‹น ํŒจํ‚ค์ง€๋ฅผ ๋”ํ•˜๊ณ , import ๊ตฌ๋ฌธ์„ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ”์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

pragma solidity ^0.8.0;

import "UniswapV3Pack/v3-core/libraries/TickMath.sol";
import "UniswapV3Pack/v3-core/interfaces/IUniswapV3Factory.sol";
import "UniswapV3Pack/v3-core/interfaces/IUniswapV3Pool.sol";
import "UniswapV3Pack/v3-core/interfaces/callback/IUniswapV3MintCallback.sol";
import "UniswapV3Pack/v3-periphery/libraries/LiquidityAmounts.sol";

๋งบ์œผ๋ฉฐ,

์ด ๊ธ€์€ ์ •๋ง ํ•„์š”์— ์˜ํ•ด ์“ฐ์˜€์Šต๋‹ˆ๋‹ค. ์ด ๊ธ€ ์ „์ฒด์— ๋‚˜ํƒ€๋‚œ bean ํ† ํฐ์„ ์œ ๋™์„ฑ ๊ณต๊ธ‰ํ•˜๋Š” ๊ณผ์ •์€, ์‹ค์ œ๋กœ ์ œ๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ๊ฐ€๊ฒฉ ๋ฒ”์œ„์—์„œ ์œ ๋™์„ฑ์„ ๊ณต๊ธ‰ํ•˜๊ธฐ ์œ„ํ•œ ๊ณผ์ •๊ณผ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์˜ํšŒ์˜ ํˆฌํ‘œ๊ถŒ์„ ์œ ๋™ํ™” ์‹œํ‚ค๋ฉด์„œ ์Šคํ…Œ์ดํ‚น ๋˜์ง€ ์•Š์€ ๊ฑฐ๋ฒ„๋„Œ์Šค ํ† ํฐ์˜ ๊ฐ€์น˜๋ฅผ ์–ต์ œํ•  ๋„๊ตฌ๋กœ์จ ํ•„์ˆ˜์ ์ด๋ผ ์ƒ๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋”๋ถˆ์–ด ํ† ํฐ์˜ ์œ ๋™์„ฑ์ด ๊ทน๋„๋กœ ๋‚ฎ๋‹ค ๋ณด๋‹ˆ ๊ฑฐ๋ฒ„๋„Œ์Šค ํ† ํฐ์— ๋Œ€ํ•œ ๊ฐ€์น˜ ํ‰๊ฐ€๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ค„์งˆ ์ˆ˜ ์—†๋Š”๋ฐ, ์œ ๋™์„ฑ์ด ํ’๋ถ€ํ•œ ํ”„๋กœํ† ์ฝœ์— ์œ ๋™์„ฑ์„ ์ง‘์ค‘์‹œ์ผœ ๊ฐ€์น˜ ํ‰๊ฐ€๊ฐ€ ์ˆ˜์›”ํ•˜๊ฒŒ ์ด๋ค„์งˆ ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋„๊ตฌ๋กœ์จ ์œ ๋‹ˆ์Šค์™‘ V3์€ ์ตœ๊ณ ์˜ ๋„๊ตฌ์˜€์œผ๋ฉฐ, ์ด๋ฅผ ํ†ตํ•ฉํ•˜๋Š” ๊ณผ์ •์€ ๋„ˆ๋ฌด ๊ณ ํ†ต์Šค๋Ÿฌ์› ์Šต๋‹ˆ๋‹ค. ์œ ๋‹ˆ์Šค์™‘์ด ๋ฐœ์ „๋˜๋ฉด์„œ, ์ดํ•ดํ•  ์ˆ˜ ์—†๋‹ค๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋Š” ์œ ํ˜•์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ๋Œ€๋ถ€๋ถ„์˜ ์—ฐ์‚ฐ์„ off-chain ์˜์—ญ์—์„œ ์ฒ˜๋ฆฌํ•˜๊ณ  on-chain์„ ํ†ตํ•ด ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘๋™ํ•˜๊ณ  ์žˆ๊ธฐ์—, ์ฝ”๋“œ์™€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์—ฐ๊ฒฐ์‹œํ‚ค๋Š” ๋ถ€๋ถ„์—์„œ๋„ ์–ด๋ ค์›€์„ ๊ฒช์„ ์ˆ˜๋ฐ–์— ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋‚˜ ์‹œ๊ฐ„์€ ๋งŽ์€ ๊ฒƒ์„ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ๋„์›€์„ ์ค๋‹ˆ๋‹ค. ์•ฝ ๋‘ ๋‹ฌ๊ฐ„ ์ •์‹ ์—†์ด ํŒŒ์•…ํ•˜๋Š” ๋™์‹œ์— ํ†ตํ•ฉํ•˜๋Š” ๊ณผ์ •์„ ๊ฒช์—ˆ๋”๋‹ˆ ๊ฝค ์œ ๋‹ˆ์Šค์™‘์ด ํŽธํ•˜๊ฒŒ ๋‹ค๊ฐ€์™”๊ธฐ์—, ์ดํ›„์˜ ์‚ฌ๋žŒ๋“ค์€ ์ œ๊ฐ€ ๊ฒช์€ ๊ณ ํ†ต์Šค๋Ÿฌ์šด ๊ณผ์ •์„ ๊ทธ๋Œ€๋กœ ๋”ฐ๋ฅด์ง€ ์•Š๊ณ  ์œ ๋‹ˆ์Šค์™‘์„ ์ข€ ๋” ์ž˜ ์ดํ•ดํ•˜๋Š” ๋งˆ์Œ์œผ๋กœ ์ด ๊ธ€์„ ์ ์—ˆ์Šต๋‹ˆ๋‹ค.

๋ถ€๋”” ์ด ๊ธ€๊ณผ ์ž๋ฃŒ๋“ค์ด ๋„์›€์ด ๋งŽ์ด ๋˜๊ธธ ๋ฐ”๋ผ๋ฉด์„œ, ์œ ๋‹ˆ์Šค์™‘์„ ํ†ตํ•ฉํ•œ ๋‹ค์–‘ํ•œ ํ”„๋กœํ† ์ฝœ๋“ค์ด ๋‚˜์˜ค๊ธฐ๋ฅผ ๊ธฐ๋Œ€ํ•ด ๋ด…๋‹ˆ๋‹ค.

Disclaimer

์ด ๊ธ€์—์„œ ๋‚˜ํƒ€๋‚˜๋Š” ๋ชจ๋“  ๋‚ด์šฉ์€ ์–ธ์ œ๋“  ๋ณ€๊ฒฝ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ฝ”๋“œ๋‚˜ ์†Œํ”„ํŠธ์›จ์–ด์˜ ์ œ์‹œ๋œ ์ž‘๋™์„ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์–ธ๊ธ‰๋œ ์ •๋ณด๋Š” ๊ต์œก ๋ชฉ์ ์ผ ๋ฟ์ด๋ฉฐ ํˆฌ์ž ์กฐ์–ธ์œผ๋กœ ๋ฐ›์•„๋“ค์—ฌ์„œ๋Š” ์•ˆ๋ฉ๋‹ˆ๋‹ค.

References